【PALWORLD】ローカルとサーバーでのプレイヤーデータの移動【備忘録】

# はじめに
この作業はいわばセーブデータの改ざんにはあたるので、ご了承ください。

また、この作業をする前には必ずバックアップを取って別ディレクトリで行うようにしてください。

そして、今回自分が行ったOSはWindowsなので、Linuxなどは確認取れていないので、ご了承ください。おそらくできるはずです。

:::note alert
重要事項
する場合は自己責任でお願いします。
:::

# 目標
この記事での目標は、ローカルプレイヤーのプレイヤーデータと、サーバーのプレイヤーデータの移動ができることです。

:::note info
前提条件
・PalServerの構築済みの環境
・Python3.9以降のインストール
・Pythonコマンドがある程度使える
・ワールドのバックアップをできていること
・文字列置き換えができるテキストエディター
:::

# 共通手順
:::note info
はじめに
サーバー側で置き換えたいプレイヤーのID(XXXXXXXX000000000000000000000000)とプレイヤー名をメモしておきましょう。
また、今後出てくる、 xxxxxxxx-0000-0000-0000-000000000000 は、この上のIDの始め8文字を小文字にしたものということです。
:::

まず、githubから`.sav`データを`.json`に変換する[プログラム](https://github.com/cheahjs/palworld-save-tools/releases/tag/v0.14.0)をダウンロードします。

ダウンロードしたら、展開して、デスクトップやドライブ直下などわかりやすい場所にフォルダーを配置します。

今回の説明では、Dドライブに配置したことにしていますが、Dドライブの場所を、配置したフォルダーとして、とらえてもらえばよいです。

`convert.py`などが入っている場所が、
```
D:\palworld-save-tools-windows-vx.x.x
```
になっていればよいです。

そのディレクトリの場所でコマンドプロンプトなり、ターミナルを開きましょう。

ここまでの作業ができたら、それぞれの項目に移動してください。
ローカルからサーバーに移行する手順
サーバーからローカルに移行する手順


# ローカルからサーバーに移行する手順
まず、プレイを進めていた、ローカルのセーブデータのフォルダーをコピーペーストで、移動します。

ワールドの判別方法は、一度選択したいワールドを起動して、日付更新順にして探すとよいです。

今回は、DドライブにPalSaveDataというフォルダーを作って移動します。

ディレクトリはこのような感じになっているはずです。

```
D:
┃
┣━━ PalSaveData
¦  ┃
¦  ┣━━ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   ¦  ┃
   ¦  ┣━━ backups
      ┃
      ┣━━ Players
      ┃  ┃
      ┃  ┣━━ 00000000000000000000000000000001.sav
      ┃  ¦
      ┃
      ┣━━ Level.sav
      ┃
      ┣━━ LevelMeta.sav
      ┃
      ┣━━ LocalData.sav
      ┃
      ┗━━ WorldOptions.sav
```
※XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXは、ワールドデータのフォルダー名

先ほど共通手順で開いたコンソールで、
次のコマンドを実行します。
```
python convert.py "D:\PalSaveData\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\Level.sav"
python convert.py "D:\PalSaveData\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\Players\00000000000000000000000000000001.sav"
```

すると、ディレクトリは次のように変わったと思います。
```
D:
┃
┣━━ PalSaveData
¦  ┃
¦  ┣━━ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   ¦  ┃
   ¦  ┣━━ backups
      ┃
      ┣━━ Players
      ┃  ┃
      ┃  ┣━━ 00000000000000000000000000000001.sav
      ┃  ┃
      ┃  ┣━━ 00000000000000000000000000000001.sav.json ※
      ┃  ¦
      ┃
      ┣━━ Level.sav
      ┃
      ┣━━ Level.sav.json ※
      ┃
      ┣━━ LevelMeta.sav
      ┃
      ┣━━ LocalData.sav
      ┃
      ┗━━ WorldOptions.sav
```
この2つのjson※の中身を書き換えます。

まず、 `00000000000000000000000000000001.sav.json` を開きます。

次に、置き換えのモードを開きます。(Windowsの場合、notepadなどを開いて、Ctrl + H)

検索する文字列に `00000000-0000-0000-0000-000000000001` を入れて、

置き換え後の文字列に `xxxxxxxx-0000-0000-0000-000000000000` 
サーバー側で置き換えたいプレイヤーIDの始めの8文字をアルファベットの場合小文字でxxxxxxxxに入れます。
すべて置き換えることができたら、保存して閉じます。

できたら、 `00000000000000000000000000000001.sav` を削除して、
共通手順で開いたコンソールに、次のコマンドを入力して、実行します。
```
python convert.py "D:\PalSaveData\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\Players\00000000000000000000000000000001.sav.json"
```
そしたら、 `00000000000000000000000000000001.sav` が生成されるので、 名前を変えて、
`XXXXXXXX000000000000000000000000.sav`にします。

:::note info
やるべきこと
[ユニコードエスケープシーケンス変換ツール](https://tech-unlimited.com/escape-unicode.html)で、プレイヤー名を入力して変換したものをコピーしておきます。

ここからは、上のテキストを `$UniPlayerName` と表記します。
:::

まず、 `Level.sav.json` を開きます。
notepad++などをおすすめします。やりこんだデータだと、とても読み込みに時間がかかるため

次に、検索のモードを開きます。(Windowsの場合、notepadなどを開いて、Ctrl + F)
`$UniPlayerName` と、入力して検索
おそらく2件ヒットすると思うので、

このような構造になっているほうを探します。

```
"players": [
	・・・中略・・・
	{
		"player_uid": "00000000-0000-0000-0000-000000000001",
		"player_info": {
			"last_online_real_time": 1091482920000,
			"player_name": "$UniPlayerName"
		}
	}
]
```
その中の `player_uid` の中身を書き換えます。
下のように
```
・・・前略・・・
"players": [
	・・・中略・・・
	{
		"player_uid": "xxxxxxxx-0000-0000-0000-000000000000",
		"player_info": {
			"last_online_real_time": 1091482920000,
			"player_name": "$UniPlayerName"
		}
	}
]
・・・後略・・・
```

もう一つのほうの、これは、おそらくプレイヤーのレベルデータなどだと思われる。
`NickName` → `value` → `$UniPlayerName` でマッチしたと思いますが、
その少し上にいったところに、 `key` があるので、その中の `PlayerUId` → `value` の中身の
`00000000-0000-0000-0000-000000000001` を `xxxxxxxx-0000-0000-0000-000000000000` に書き換えます。
そして、同じく、 `key` の中の `InstanceId` → `value` の中身をコピーして、検索します。

```
・・・前略・・・
},
{
	"key": {
		"PlayerUId": {
			"struct_type": "Guid",
			"struct_id": "00000000-0000-0000-0000-000000000000",
			"id": null,
			"value": "00000000-0000-0000-0000-000000000001",
			"type": "StructProperty"
		},
		"InstanceId": {
			"struct_type": "Guid",
			"struct_id": "00000000-0000-0000-0000-000000000000",
			"id": null,
			"value": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
			"type": "StructProperty"
		},
		"DebugName": {
			"id": null,
			"value": "",
			"type": "StrProperty"
		}
	},
	"value": {
		"RawData": {
			"array_type": "ByteProperty",
			"id": null,
			"value": {
				"object": {
					"SaveParameter": {
						"struct_type": "PalIndividualCharacterSaveParameter",
						"struct_id": "00000000-0000-0000-0000-000000000000",
						"id": null,
						"value": {
							・・・中略・・・
							"NickName": {
								"id": null,
								"value": "$UniPlayerName",
								"type": "StrProperty"
							},
							・・・後略・・・
```

検索して出た、 `instance_id` の上の　guid` を xxxxxxxx-0000-0000-0000-000000000000 に書き換えます。
```
・・・前略・・・
{
	"guid": "00000000-0000-0000-0000-000000000001",
    "instance_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
・・・後略・・・
```

保存して閉じます。

できたら、 `Level.sav` を削除して、

次のコマンドを実行します。
```
python convert.py "D:\PalSaveData\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\Level.sav.json"
```

すると、`Level.sav` が生成されます。

あとは、`.sav.json`のファイルを削除して、`Players`の中の`00000000000000000000000000000001.sav`の名前をサーバー側のプレイヤーデータのID(XXXXXXXX000000000000000000000000.sav)に変更出来たらセーブデータの書き換え自体は完了です。

あとは、フォルダーの中のデータをサーバー側に移動したら、恐らく移行が出来ているはずです。

# サーバーからローカルに移行する手順

ここでは、省略させていただきますが、

`ローカルからサーバーに移行する手順`のサーバー側のIDとローカル側のIDを逆にしてすることで、できるはずです。

# まとめ
jsonにすることで、サーバーと、プレイヤーデータの移動ができることが分かった。

# 追記
~~この方法では、パルのデータが移動できないことが分かったため、
分かり次第追記します。~~
パルのデータを引き継ぐことができたため、追記しました。

# 参考文献

https://github.com/cheahjs/palworld-save-tools