hytale 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +21 -0
- data/README.md +1430 -15
- data/exe/hytale +497 -0
- data/lib/hytale/client/assets.rb +207 -0
- data/lib/hytale/client/block_type.rb +169 -0
- data/lib/hytale/client/config.rb +98 -0
- data/lib/hytale/client/cosmetics.rb +95 -0
- data/lib/hytale/client/item_type.rb +248 -0
- data/lib/hytale/client/launcher_log/launcher_log_entry.rb +58 -0
- data/lib/hytale/client/launcher_log/launcher_log_session.rb +57 -0
- data/lib/hytale/client/launcher_log.rb +99 -0
- data/lib/hytale/client/locale.rb +234 -0
- data/lib/hytale/client/map/block.rb +135 -0
- data/lib/hytale/client/map/chunk.rb +695 -0
- data/lib/hytale/client/map/marker.rb +50 -0
- data/lib/hytale/client/map/region.rb +278 -0
- data/lib/hytale/client/map/renderer.rb +435 -0
- data/lib/hytale/client/map.rb +271 -0
- data/lib/hytale/client/memories.rb +59 -0
- data/lib/hytale/client/npc_memory.rb +39 -0
- data/lib/hytale/client/permissions.rb +52 -0
- data/lib/hytale/client/player/entity_stats.rb +46 -0
- data/lib/hytale/client/player/inventory.rb +54 -0
- data/lib/hytale/client/player/item.rb +102 -0
- data/lib/hytale/client/player/item_storage.rb +40 -0
- data/lib/hytale/client/player/player_memory.rb +29 -0
- data/lib/hytale/client/player/position.rb +12 -0
- data/lib/hytale/client/player/rotation.rb +11 -0
- data/lib/hytale/client/player/vector3.rb +12 -0
- data/lib/hytale/client/player.rb +101 -0
- data/lib/hytale/client/player_skin.rb +179 -0
- data/lib/hytale/client/prefab/palette_entry.rb +49 -0
- data/lib/hytale/client/prefab.rb +184 -0
- data/lib/hytale/client/process.rb +57 -0
- data/lib/hytale/client/save/backup.rb +43 -0
- data/lib/hytale/client/save/server_log.rb +42 -0
- data/lib/hytale/client/save.rb +157 -0
- data/lib/hytale/client/settings/audio_settings.rb +30 -0
- data/lib/hytale/client/settings/builder_tools_settings.rb +27 -0
- data/lib/hytale/client/settings/gameplay_settings.rb +23 -0
- data/lib/hytale/client/settings/input_bindings.rb +25 -0
- data/lib/hytale/client/settings/mouse_settings.rb +23 -0
- data/lib/hytale/client/settings/rendering_settings.rb +30 -0
- data/lib/hytale/client/settings.rb +74 -0
- data/lib/hytale/client/world/client_effects.rb +24 -0
- data/lib/hytale/client/world/death_settings.rb +22 -0
- data/lib/hytale/client/world.rb +88 -0
- data/lib/hytale/client/zone/region.rb +52 -0
- data/lib/hytale/client/zone.rb +68 -0
- data/lib/hytale/client.rb +142 -0
- data/lib/hytale/server/process.rb +11 -0
- data/lib/hytale/server.rb +6 -0
- data/lib/hytale/version.rb +1 -1
- data/lib/hytale.rb +37 -2
- metadata +119 -10
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Memories
|
|
6
|
+
attr_reader :data, :path
|
|
7
|
+
|
|
8
|
+
def initialize(data, path: nil)
|
|
9
|
+
@data = data
|
|
10
|
+
@path = path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def all
|
|
14
|
+
@all ||= (data["Memories"] || []).map { |mem| NPCMemory.new(mem) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def count
|
|
18
|
+
all.size
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_by_role(role)
|
|
22
|
+
all.find { |mem| mem.npc_role == role }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find_all_by_location(location)
|
|
26
|
+
all.select { |mem| mem.location&.include?(location) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def roles
|
|
30
|
+
all.map(&:npc_role).uniq.sort
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def locations
|
|
34
|
+
all.map(&:location).compact.uniq.sort
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def each(&)
|
|
38
|
+
all.each(&)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
include Enumerable
|
|
42
|
+
|
|
43
|
+
def to_a = all
|
|
44
|
+
def to_h = data
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
def load(path)
|
|
48
|
+
raise NotFoundError, "Memories file not found: #{path}" unless File.exist?(path)
|
|
49
|
+
|
|
50
|
+
json = File.read(path)
|
|
51
|
+
data = JSON.parse(json)
|
|
52
|
+
new(data, path: path)
|
|
53
|
+
rescue JSON::ParserError => e
|
|
54
|
+
raise ParseError, "Failed to parse memories: #{e.message}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class NPCMemory
|
|
6
|
+
attr_reader :data
|
|
7
|
+
|
|
8
|
+
def initialize(data)
|
|
9
|
+
@data = data
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def id = data["Id"]
|
|
13
|
+
def npc_role = data["NPCRole"]
|
|
14
|
+
def translation_key = data["TranslationKey"]
|
|
15
|
+
def name_overridden? = data["IsMemoriesNameOverridden"]
|
|
16
|
+
|
|
17
|
+
def captured_at
|
|
18
|
+
timestamp = data["CapturedTimestamp"]
|
|
19
|
+
timestamp ? Time.at(timestamp / 1000.0) : nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def location_key = data["FoundLocationNameKey"]
|
|
23
|
+
|
|
24
|
+
def location
|
|
25
|
+
location_key&.split(".")&.last
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def friendly_name
|
|
29
|
+
npc_role&.gsub("_", " ")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
"#{friendly_name} found at #{location}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_h = data
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Permissions
|
|
6
|
+
attr_reader :data, :path
|
|
7
|
+
|
|
8
|
+
def initialize(data, path: nil)
|
|
9
|
+
@data = data
|
|
10
|
+
@path = path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def users
|
|
14
|
+
data["users"] || {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def groups
|
|
18
|
+
data["groups"] || {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def user_groups(uuid)
|
|
22
|
+
users.dig(uuid, "groups") || []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def group_permissions(group_name)
|
|
26
|
+
groups[group_name] || []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def user_permissions(uuid)
|
|
30
|
+
user_groups(uuid).flat_map { |g| group_permissions(g) }.uniq
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def op?(uuid)
|
|
34
|
+
user_groups(uuid).include?("OP") || user_permissions(uuid).include?("*")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_h = data
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
def load(path)
|
|
41
|
+
raise NotFoundError, "Permissions file not found: #{path}" unless File.exist?(path)
|
|
42
|
+
|
|
43
|
+
json = File.read(path)
|
|
44
|
+
data = JSON.parse(json)
|
|
45
|
+
new(data, path: path)
|
|
46
|
+
rescue JSON::ParserError => e
|
|
47
|
+
raise ParseError, "Failed to parse permissions: #{e.message}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
class EntityStats
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def initialize(data)
|
|
10
|
+
@data = data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def version = data["Version"]
|
|
14
|
+
|
|
15
|
+
def health
|
|
16
|
+
stat("Health")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def max_health
|
|
20
|
+
base = health || 0
|
|
21
|
+
modifier = stat_modifier("Health", "Armor_ADDITIVE") || 0
|
|
22
|
+
|
|
23
|
+
base + modifier
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stamina = stat("Stamina")
|
|
27
|
+
def oxygen = stat("Oxygen")
|
|
28
|
+
def mana = stat("Mana")
|
|
29
|
+
|
|
30
|
+
def stat(name)
|
|
31
|
+
data.dig("Stats", name, "Value")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stat_modifier(stat_name, modifier_name)
|
|
35
|
+
data.dig("Stats", stat_name, "Modifiers", modifier_name, "Amount")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def all_stats
|
|
39
|
+
(data["Stats"] || {}).transform_values { |v| v["Value"] }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_h = data
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
class Inventory
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def initialize(data)
|
|
10
|
+
@data = data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def version = data["Version"]
|
|
14
|
+
|
|
15
|
+
def storage
|
|
16
|
+
@storage ||= ItemStorage.new(data["Storage"] || {})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def backpack
|
|
20
|
+
@backpack ||= ItemStorage.new(data["Backpack"] || {})
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def hotbar
|
|
24
|
+
@hotbar ||= ItemStorage.new(data["HotBar"] || {})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def armor
|
|
28
|
+
@armor ||= ItemStorage.new(data["Armor"] || {})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def utility
|
|
32
|
+
@utility ||= ItemStorage.new(data["Utility"] || {})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def tools
|
|
36
|
+
@tools ||= ItemStorage.new(data["Tool"] || {})
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def active_hotbar_slot = data["ActiveHotbarSlot"]
|
|
40
|
+
def sort_type = data["SortType"]
|
|
41
|
+
|
|
42
|
+
def backpack?
|
|
43
|
+
backpack&.simple?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def all_items
|
|
47
|
+
[storage, backpack, hotbar, armor, utility, tools].flat_map(&:items)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_h = data
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
class Item
|
|
7
|
+
attr_reader :data, :slot
|
|
8
|
+
|
|
9
|
+
def initialize(data, slot: nil)
|
|
10
|
+
@data = data
|
|
11
|
+
@slot = slot
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def id = data["Id"]
|
|
15
|
+
def name = id&.gsub("_", " ")
|
|
16
|
+
def type = ItemType.find(id) || ItemType.new(id)
|
|
17
|
+
def quantity = data["Quantity"]
|
|
18
|
+
def durability = data["Durability"]
|
|
19
|
+
def max_durability = data["MaxDurability"]
|
|
20
|
+
|
|
21
|
+
def durability_percent
|
|
22
|
+
return nil unless durability && max_durability&.positive?
|
|
23
|
+
|
|
24
|
+
(durability / max_durability * 100).round(1)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def damaged?
|
|
28
|
+
durability && max_durability && durability < max_durability
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def icon_path
|
|
32
|
+
find_icon_path || Assets.item_icon_path(id)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def icon_exists?
|
|
36
|
+
!!find_icon_path
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def find_icon_path
|
|
42
|
+
path = Assets.item_icon_path(id)
|
|
43
|
+
return path if File.exist?(path)
|
|
44
|
+
|
|
45
|
+
variations = generate_icon_variations(id)
|
|
46
|
+
|
|
47
|
+
variations.each do |variation|
|
|
48
|
+
path = Assets.item_icon_path(variation)
|
|
49
|
+
return path if File.exist?(path)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
alternate_paths = generate_alternate_paths(id)
|
|
53
|
+
|
|
54
|
+
alternate_paths.each do |alt_path|
|
|
55
|
+
full_path = Assets.cached_path(alt_path)
|
|
56
|
+
return full_path if File.exist?(full_path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def generate_icon_variations(item_id)
|
|
63
|
+
variations = []
|
|
64
|
+
|
|
65
|
+
variations << item_id.gsub("Shortbow", "Bow") if item_id.include?("Shortbow")
|
|
66
|
+
|
|
67
|
+
variations << item_id.gsub("Longbow", "Bow") if item_id.include?("Longbow")
|
|
68
|
+
|
|
69
|
+
variations << item_id.gsub("Longsword", "Sword") if item_id.include?("Longsword")
|
|
70
|
+
|
|
71
|
+
variations
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def generate_alternate_paths(item_id)
|
|
75
|
+
paths = []
|
|
76
|
+
|
|
77
|
+
# EditorTool_Paint -> Common/Icons/Items/EditorTools/Paint.png
|
|
78
|
+
if item_id.start_with?("EditorTool_")
|
|
79
|
+
tool_name = item_id.sub("EditorTool_", "")
|
|
80
|
+
|
|
81
|
+
paths << "Common/Icons/Items/EditorTools/#{tool_name}.png"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
paths
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
public
|
|
88
|
+
|
|
89
|
+
def to_s
|
|
90
|
+
string = name.to_s
|
|
91
|
+
|
|
92
|
+
string += " x#{quantity}" if quantity && quantity > 1
|
|
93
|
+
string += " (#{durability_percent}%)" if durability_percent && durability_percent < 100
|
|
94
|
+
|
|
95
|
+
string
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def to_h = data
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
class ItemStorage
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def initialize(data)
|
|
10
|
+
@data = data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def capacity = data["Capacity"]
|
|
14
|
+
def type = data["Id"]
|
|
15
|
+
|
|
16
|
+
def empty?
|
|
17
|
+
type == "Empty"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def simple?
|
|
21
|
+
type == "Simple"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def items
|
|
25
|
+
(data["Items"] || {}).map do |slot, item_data|
|
|
26
|
+
Item.new(item_data, slot: slot.to_i)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def [](slot)
|
|
31
|
+
item_data = data.dig("Items", slot.to_s)
|
|
32
|
+
|
|
33
|
+
Item.new(item_data, slot: slot) if item_data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_h = data
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
class PlayerMemory
|
|
7
|
+
attr_reader :data
|
|
8
|
+
|
|
9
|
+
def initialize(data)
|
|
10
|
+
@data = data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def id = data["Id"]
|
|
14
|
+
def npc_role = data["NPCRole"]
|
|
15
|
+
def translation_key = data["TranslationKey"]
|
|
16
|
+
def name_overridden? = data["IsMemoriesNameOverridden"]
|
|
17
|
+
def captured_at = data["CapturedTimestamp"] ? Time.at(data["CapturedTimestamp"] / 1000.0) : nil
|
|
18
|
+
def location_key = data["FoundLocationNameKey"]
|
|
19
|
+
|
|
20
|
+
def location
|
|
21
|
+
location_key&.split(".")&.last
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s = "#{npc_role} (#{location})"
|
|
25
|
+
def to_h = data
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
Vector3 = Data.define(:x, :y, :z) do
|
|
7
|
+
def to_s = "(#{x&.round(4)}, #{y&.round(4)}, #{z&.round(4)})"
|
|
8
|
+
def magnitude = Math.sqrt(((x || 0)**2) + ((y || 0)**2) + ((z || 0)**2))
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hytale
|
|
4
|
+
module Client
|
|
5
|
+
class Player
|
|
6
|
+
attr_reader :data, :uuid, :path
|
|
7
|
+
|
|
8
|
+
def initialize(data, uuid:, path: nil)
|
|
9
|
+
@data = data
|
|
10
|
+
@uuid = uuid
|
|
11
|
+
@path = path
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def components
|
|
15
|
+
data["Components"] || {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def name
|
|
19
|
+
components.dig("Nameplate", "Text") || components.dig("DisplayName", "DisplayName", "RawText")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def position
|
|
23
|
+
transform = components["Transform"] || {}
|
|
24
|
+
pos = transform["Position"] || {}
|
|
25
|
+
Position.new(pos["X"], pos["Y"], pos["Z"])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def rotation
|
|
29
|
+
transform = components["Transform"] || {}
|
|
30
|
+
rot = transform["Rotation"] || {}
|
|
31
|
+
Rotation.new(rot["Pitch"], rot["Yaw"], rot["Roll"])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def velocity
|
|
35
|
+
vel = components.dig("Velocity", "Velocity") || {}
|
|
36
|
+
Vector3.new(vel["X"], vel["Y"], vel["Z"])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def stats
|
|
40
|
+
@stats ||= EntityStats.new(components["EntityStats"] || {})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def inventory
|
|
44
|
+
@inventory ||= Inventory.new(components.dig("Player", "Inventory") || {})
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def player_data
|
|
48
|
+
components["Player"] || {}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def game_mode
|
|
52
|
+
player_data["GameMode"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def current_world
|
|
56
|
+
player_data.dig("PlayerData", "World")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def discovered_zones
|
|
60
|
+
@discovered_zones ||= (player_data.dig("PlayerData", "DiscoveredZones") || []).map do |id|
|
|
61
|
+
Hytale::Client::Zone::Region.new(id)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def respawn_points
|
|
66
|
+
player_data.dig("PlayerData", "PerWorldData", "default", "RespawnPoints") || []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def memories
|
|
70
|
+
@memories ||= (components.dig("PlayerMemories", "Memories") || []).map do |mem|
|
|
71
|
+
PlayerMemory.new(mem)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def skin
|
|
76
|
+
@skin ||= PlayerSkin.find(uuid)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def avatar_preview_path
|
|
80
|
+
skin&.avatar_preview_path
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def to_h
|
|
84
|
+
data
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class << self
|
|
88
|
+
def load(path)
|
|
89
|
+
raise NotFoundError, "Player file not found: #{path}" unless File.exist?(path)
|
|
90
|
+
|
|
91
|
+
uuid = File.basename(path, ".json")
|
|
92
|
+
json = File.read(path)
|
|
93
|
+
data = JSON.parse(json)
|
|
94
|
+
new(data, uuid: uuid, path: path)
|
|
95
|
+
rescue JSON::ParserError => e
|
|
96
|
+
raise ParseError, "Failed to parse player data: #{e.message}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|