hytale 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca11b549d14d2bd553261112ed5d9397d3521677ea5ecbd4595960c821e805e6
4
- data.tar.gz: 6f52dcc127393200cfdf3cd3332c5404dbfa168c02e3299b24c5e444450b4a1c
3
+ metadata.gz: 00d6560e486235a55ab2450d656b61421b6fddb8b9b40c3c3226a1df754f8724
4
+ data.tar.gz: 81194f4a57ffb54903a48f2d6361e101ab35049245cd654e7631e81f30949187
5
5
  SHA512:
6
- metadata.gz: 29a3bcf4aad9b01adc248bc014b2cede5221950b72152e3e18add25ba7858776885ddfaf799406f0da88ace286abb20577cd30e1786c37f4348783cb7c0db388
7
- data.tar.gz: 34f3c99f23607f0e5ef6577ca4a0699037591302bafb58865392d9cd4293cbaa878938e1eee8c222207407d179389620c2f07a77f9c5af6d772d82eb8b95ac75
6
+ metadata.gz: 80355bb885697efd829c1807ba7ae3d91436fb654ee1a40f0af1a4d3de0a5f6794ffa0801e3b06f5f832b8b01b7d27cccc25c0d8b96882fee57a32eb283696af
7
+ data.tar.gz: d219d3d0363f5e4fd79d1affb96e272c53a5e3565d0ede2a9a2837e269aa449441324dbd55b44c9f6dd07c1bf2930ca415baffe29e1926b2ba8a3590f22ca9ef
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <p>
6
6
  <a href="https://rubygems.org/gems/hytale"><img alt="Gem Version" src="https://img.shields.io/gem/v/hytale"></a>
7
- <a href="https://github.com/marcoroth/hytale-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/hytale"></a>
7
+ <a href="https://github.com/marcoroth/hytale-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/hytale-ruby"></a>
8
8
  </p>
9
9
 
10
10
  <p>Read and parse Hytale game data including settings, saves, players, and launcher logs.<br/>Cross-platform support for macOS, Windows, and Linux.</p>
@@ -295,10 +295,10 @@ save = Hytale.client.save("New World")
295
295
  player = save.players.first
296
296
 
297
297
  player.name # => "marcoroth"
298
- player.uuid # => "79816d74-0500-4dad-9767-06af86c17243"
298
+ player.uuid # => "00000000-0000-0000-0000-000000000000"
299
299
  player.position # => (590.75, 123.0, 374.2)
300
300
  player.game_mode # => "Adventure"
301
- player.discovered_zones # => ["Zone1_Spawn", "Zone1_Tier1", ...]
301
+ player.discovered_zones # => [Zone::Region, Zone::Region, ...]
302
302
  player.skin # => PlayerSkin object
303
303
  player.avatar_preview_path # => "/path/to/CachedAvatarPreviews/uuid.png"
304
304
  ```
@@ -333,10 +333,24 @@ end
333
333
  |------|-------------|
334
334
  | `hotbar` | 9-slot quick access bar |
335
335
  | `storage` | Main inventory (36 slots) |
336
+ | `backpack` | Optional backpack storage (if equipped) |
336
337
  | `armor` | Head, chest, hands, legs |
337
338
  | `utility` | Utility items (4 slots) |
338
339
  | `tools` | Builder/editor tools |
339
340
 
341
+ **Check if player has a backpack:**
342
+
343
+ ```ruby
344
+ player.inventory.backpack? # => true/false
345
+ ```
346
+
347
+ **ItemStorage type checks:**
348
+
349
+ ```ruby
350
+ player.inventory.backpack.empty? # => true (type is "Empty" - no backpack equipped)
351
+ player.inventory.backpack.simple? # => true (type is "Simple" - backpack equipped)
352
+ ```
353
+
340
354
  **Item properties:**
341
355
 
342
356
  ```ruby
@@ -349,6 +363,79 @@ item.durability_percent # => 14.9
349
363
  item.damaged? # => true
350
364
  ```
351
365
 
366
+ ### Zones and Regions
367
+
368
+ Hytale organizes the world into zones (biomes) and regions (areas within zones):
369
+
370
+ **Zones (biomes):**
371
+
372
+ **List all zones (requires game to be installed):**
373
+
374
+ ```ruby
375
+ Hytale::Client::Zone.all
376
+ # => [#<Zone id="Emerald_Wilds">, #<Zone id="Howling_Sands">, ...]
377
+ ```
378
+
379
+ **Find a specific zone:**
380
+
381
+ ```ruby
382
+ zone = Hytale::Client::Zone.find("Emerald_Wilds")
383
+ zone.id # => "Emerald_Wilds"
384
+ zone.name # => "Emerald Wilds"
385
+ ```
386
+
387
+ **Get all regions in a zone:**
388
+
389
+ ```ruby
390
+ zone.regions
391
+ # => [#<Zone::Region id="Zone1_Spawn">, #<Zone::Region id="Zone1_Tier1">, ...]
392
+ ```
393
+
394
+ **Regions (areas within zones):**
395
+
396
+ **List all regions:**
397
+
398
+ ```ruby
399
+ Hytale::Client::Zone::Region.all
400
+ # => [#<Zone::Region id="Zone1_Spawn">, #<Zone::Region id="Zone1_Tier1">, ...]
401
+ ```
402
+
403
+ **Find a specific region:**
404
+
405
+ ```ruby
406
+ region = Hytale::Client::Zone::Region.find("Zone1_Tier1")
407
+ region.id # => "Zone1_Tier1"
408
+ region.name # => "Drifting Plains"
409
+ region.region_name # => "Drifting Plains"
410
+ ```
411
+
412
+ **Navigate to parent zone:**
413
+
414
+ ```ruby
415
+ region.zone # => #<Zone id="Emerald_Wilds">
416
+ region.zone.name # => "Emerald Wilds"
417
+ ```
418
+
419
+ **Player discovered zones:**
420
+
421
+ ```ruby
422
+ player.discovered_zones.each do |region|
423
+ puts "#{region.name} (#{region.zone.name})"
424
+ end
425
+ # => First Gate of the Echo (Emerald Wilds)
426
+ # => Drifting Plains (Emerald Wilds)
427
+ ```
428
+
429
+ **Zone/Region mapping:**
430
+
431
+ | Zone | Region Prefix | Example Regions |
432
+ |------|---------------|-----------------|
433
+ | Emerald Wilds | Zone1_* | Zone1_Spawn, Zone1_Tier1, Zone1_Tier2, Zone1_Tier3 |
434
+ | Howling Sands | Zone2_* | Zone2_Tier1, Zone2_Tier2, Zone2_Tier3 |
435
+ | Whisperfrost Frontiers | Zone3_* | Zone3_Tier1, Zone3_Tier2, Zone3_Tier3 |
436
+ | Devastated Lands | Zone4_* | Zone4_Tier4, Zone4_Tier5 |
437
+ | Oceans | Oceans | Oceans |
438
+
352
439
  ### Memories (Discovered Creatures)
353
440
 
354
441
  Track discovered NPCs and creatures:
@@ -625,8 +712,8 @@ chunk = region.each_chunk.first
625
712
 
626
713
  chunk.block_types # => ["Rock_Stone", "Soil_Dirt", "Soil_Grass", ...]
627
714
  chunk.terrain_type # => :grassland
628
- chunk.has_water? # => false
629
- chunk.has_vegetation? # => true
715
+ chunk.water? # => false
716
+ chunk.vegetation? # => true
630
717
  chunk.local_x # => 15
631
718
  chunk.local_z # => 8
632
719
  chunk.world_x # => -272
@@ -793,6 +880,34 @@ process.pid # => 12345
793
880
  | `player_skins` | List all cached PlayerSkin objects |
794
881
  | `player_skin(uuid)` | Find PlayerSkin by UUID |
795
882
 
883
+ ### Hytale::Client::Zone
884
+
885
+ | Method | Description |
886
+ |--------|-------------|
887
+ | `all` | List all Zone objects (from locale) |
888
+ | `find(id)` | Find Zone by ID, returns nil if not found |
889
+ | `new(id)` | Create a Zone::Base instance |
890
+
891
+ ### Hytale::Client::Zone::Base
892
+
893
+ | Method | Description |
894
+ |--------|-------------|
895
+ | `id` | Zone ID (e.g., "Emerald_Wilds") |
896
+ | `name` | Translated zone name (e.g., "Emerald Wilds") |
897
+ | `regions` | All Region objects belonging to this zone |
898
+
899
+ ### Hytale::Client::Zone::Region
900
+
901
+ | Method | Description |
902
+ |--------|-------------|
903
+ | `all` | List all Region objects (from locale) |
904
+ | `find(id)` | Find Region by ID, returns nil if not found |
905
+ | `id` | Region ID (e.g., "Zone1_Tier1") |
906
+ | `name` | Translated region name (e.g., "Drifting Plains") |
907
+ | `region_name` | Same as `name` |
908
+ | `zone` | Parent Zone::Base object |
909
+ | `zone_name` | Parent zone's translated name |
910
+
796
911
  ### Hytale::Client::Map
797
912
 
798
913
  | Method | Description |
@@ -839,8 +954,8 @@ process.pid # => 12345
839
954
  | `block_types` | Block type IDs found in chunk |
840
955
  | `block_palette` | Parsed palette (index → block name) |
841
956
  | `terrain_type` | Detected terrain (`:grassland`, `:water`, etc.) |
842
- | `has_water?` | Contains water blocks |
843
- | `has_vegetation?` | Contains plant/grass blocks |
957
+ | `water?` | Contains water blocks |
958
+ | `vegetation?` | Contains plant/grass blocks |
844
959
  | `to_ascii_map` | 16x16 ASCII representation |
845
960
 
846
961
  ### Hytale::Client::Map::Block
@@ -1169,8 +1284,8 @@ Hytale.client.player_skins
1169
1284
  **Find a specific skin:**
1170
1285
 
1171
1286
  ```ruby
1172
- skin = Hytale.client.player_skin("79816d74-0500-4dad-9767-06af86c17243")
1173
- skin.uuid # => "79816d74-0500-4dad-9767-06af86c17243"
1287
+ skin = Hytale.client.player_skin("00000000-0000-0000-0000-000000000000")
1288
+ skin.uuid # => "00000000-0000-0000-0000-000000000000"
1174
1289
  ```
1175
1290
 
1176
1291
  **Appearance:**
@@ -24,7 +24,9 @@ module Hytale
24
24
  def profile_uuid
25
25
  profile_entry = entries.find { |e| e.message&.include?("setting current profile") }
26
26
 
27
- profile_entry&.message&.match(/to (\S+)/)&.[](1)
27
+ return nil unless profile_entry
28
+
29
+ profile_entry.message&.match(/to (\S+)/)&.[](1)
28
30
  end
29
31
 
30
32
  def game_launched?
@@ -48,12 +48,18 @@ module Hytale
48
48
 
49
49
  def current_profile_uuid
50
50
  profile_entry = entries.reverse.find { |e| e.message&.include?("setting current profile") }
51
- profile_entry&.message&.match(/to (\S+)/)&.[](1)
51
+
52
+ return nil unless profile_entry
53
+
54
+ profile_entry.message&.match(/to (\S+)/)&.[](1)
52
55
  end
53
56
 
54
57
  def current_channel
55
58
  channel_entry = entries.reverse.find { |e| e.message&.include?("setting channel") }
56
- channel_entry&.attributes&.[]("channel")
59
+
60
+ return nil unless channel_entry
61
+
62
+ channel_entry.attributes&.[]("channel")
57
63
  end
58
64
 
59
65
  def last_game_launch
@@ -61,7 +67,6 @@ module Hytale
61
67
  end
62
68
 
63
69
  def sessions
64
- # Group entries by launcher start
65
70
  sessions = []
66
71
  current_session = nil
67
72
 
@@ -63,26 +63,26 @@ module Hytale
63
63
  @block_palette ||= extract_palette
64
64
  end
65
65
 
66
- def has_block?(block_type)
66
+ def block?(block_type)
67
67
  block_types.include?(block_type)
68
68
  end
69
69
 
70
- def has_water?
70
+ def water?
71
71
  block_types.any? { |t| t.include?("Water") }
72
72
  end
73
73
 
74
- def has_vegetation?
74
+ def vegetation?
75
75
  block_types.any? { |t| t.include?("Plant") || t.include?("Grass") || t.include?("Tree") }
76
76
  end
77
77
 
78
78
  def terrain_type
79
- if has_water?
79
+ if water?
80
80
  :water
81
81
  elsif block_types.any? { |t| t.include?("Sand") }
82
82
  :desert
83
83
  elsif block_types.any? { |t| t.include?("Snow") || t.include?("Ice") }
84
84
  :snow
85
- elsif has_vegetation?
85
+ elsif vegetation?
86
86
  :grassland
87
87
  else
88
88
  :rocky
@@ -195,9 +195,7 @@ module Hytale
195
195
  def cache_path(texture_scale: 16, shading: true)
196
196
  return nil unless region
197
197
 
198
- save_name = region.path.split("/").find do |p|
199
- p.include?("Saves")
200
- end&.then { |_| region.path.split("Saves/")[1]&.split("/")&.first } || "unknown"
198
+ save_name = region.path.split("/").find { |p| p.include?("Saves") }&.then { |_| region.path.split("Saves/")[1]&.split("/")&.first } || "unknown"
201
199
  world_name = region.path.split("/worlds/")[1]&.split("/")&.first || "default"
202
200
 
203
201
  cache_dir = File.join(
@@ -217,14 +215,17 @@ module Hytale
217
215
 
218
216
  def cached?(texture_scale: 16, shading: true)
219
217
  path = cache_path(texture_scale: texture_scale, shading: shading)
218
+
220
219
  path && File.exist?(path)
221
220
  end
222
221
 
223
222
  def clear_cache!
224
223
  path = cache_path
224
+
225
225
  return unless path
226
226
 
227
227
  dir = File.dirname(path)
228
+
228
229
  FileUtils.rm_rf(dir) if File.directory?(dir)
229
230
  end
230
231
 
@@ -238,6 +239,7 @@ module Hytale
238
239
  return nil unless type_id
239
240
 
240
241
  block_type = get_or_create_block_type(type_id)
242
+
241
243
  Block.new(block_type, x: x, y: y, z: z, chunk: self)
242
244
  end
243
245
 
@@ -643,7 +645,7 @@ module Hytale
643
645
  index_position = position + 1 + length + 2
644
646
  index = begin
645
647
  search_data[index_position].ord
646
- rescue StandardError
648
+ rescue StandardError # rubocop:disable Metrics/BlockNesting
647
649
  nil
648
650
  end
649
651
 
@@ -415,12 +415,10 @@ module Hytale
415
415
  ChunkyPNG::Color.rgb(194, 178, 128)
416
416
  when /Snow/, /Ice/
417
417
  ChunkyPNG::Color.rgb(240, 240, 255)
418
- when /Dirt/
418
+ when /Dirt/, /Wood/
419
419
  ChunkyPNG::Color.rgb(139, 90, 43)
420
420
  when /Stone/, /Rock/
421
421
  ChunkyPNG::Color.rgb(128, 128, 128)
422
- when /Wood/
423
- ChunkyPNG::Color.rgb(139, 90, 43)
424
422
  when /Plant/
425
423
  ChunkyPNG::Color.rgb(34, 139, 34)
426
424
  when /Ore/
@@ -16,6 +16,10 @@ module Hytale
16
16
  @storage ||= ItemStorage.new(data["Storage"] || {})
17
17
  end
18
18
 
19
+ def backpack
20
+ @backpack ||= ItemStorage.new(data["Backpack"] || {})
21
+ end
22
+
19
23
  def hotbar
20
24
  @hotbar ||= ItemStorage.new(data["HotBar"] || {})
21
25
  end
@@ -35,8 +39,12 @@ module Hytale
35
39
  def active_hotbar_slot = data["ActiveHotbarSlot"]
36
40
  def sort_type = data["SortType"]
37
41
 
42
+ def backpack?
43
+ backpack&.simple?
44
+ end
45
+
38
46
  def all_items
39
- [storage, hotbar, armor, utility, tools].flat_map(&:items)
47
+ [storage, backpack, hotbar, armor, utility, tools].flat_map(&:items)
40
48
  end
41
49
 
42
50
  def to_h = data
@@ -13,6 +13,14 @@ module Hytale
13
13
  def capacity = data["Capacity"]
14
14
  def type = data["Id"]
15
15
 
16
+ def empty?
17
+ type == "Empty"
18
+ end
19
+
20
+ def simple?
21
+ type == "Simple"
22
+ end
23
+
16
24
  def items
17
25
  (data["Items"] || {}).map do |slot, item_data|
18
26
  Item.new(item_data, slot: slot.to_i)
@@ -57,7 +57,9 @@ module Hytale
57
57
  end
58
58
 
59
59
  def discovered_zones
60
- player_data.dig("PlayerData", "DiscoveredZones") || []
60
+ @discovered_zones ||= (player_data.dig("PlayerData", "DiscoveredZones") || []).map do |id|
61
+ Hytale::Client::Zone::Region.new(id)
62
+ end
61
63
  end
62
64
 
63
65
  def respawn_points
@@ -10,7 +10,6 @@ module Hytale
10
10
  @path = path
11
11
  end
12
12
 
13
- # Display settings
14
13
  def fullscreen? = data["Fullscreen"]
15
14
  def maximized? = data["Maximized"]
16
15
  def window_width = data["WindowWidth"]
@@ -21,12 +20,10 @@ module Hytale
21
20
  def unlimited_fps? = data["UnlimitedFps"]
22
21
  def field_of_view = data["FieldOfView"]
23
22
 
24
- # Rendering settings
25
23
  def rendering
26
24
  @rendering ||= RenderingSettings.new(data["RenderingSettings"] || {})
27
25
  end
28
26
 
29
- # Input settings
30
27
  def input_bindings
31
28
  @input_bindings ||= InputBindings.new(data["InputBindings"] || {})
32
29
  end
@@ -35,22 +32,18 @@ module Hytale
35
32
  @mouse_settings ||= MouseSettings.new(data["MouseSettings"] || {})
36
33
  end
37
34
 
38
- # Audio settings
39
35
  def audio
40
36
  @audio ||= AudioSettings.new(data["AudioSettings"] || {})
41
37
  end
42
38
 
43
- # Gameplay settings
44
39
  def gameplay
45
40
  @gameplay ||= GameplaySettings.new(data["GameplaySettings"] || {})
46
41
  end
47
42
 
48
- # Builder tools settings
49
43
  def builder_tools
50
44
  @builder_tools ||= BuilderToolsSettings.new(data["BuilderToolsSettings"] || {})
51
45
  end
52
46
 
53
- # UI preferences
54
47
  def hide_hud? = data["HideHud"]
55
48
  def hide_hotbar? = data["HideHotbar"]
56
49
  def hide_compass? = data["HideCompass"]
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hytale
4
+ module Client
5
+ module Zone
6
+ class Region
7
+ class << self
8
+ def all
9
+ Locale.regions.keys.map { |id| new(id) }
10
+ end
11
+
12
+ def find(id)
13
+ new(id) if Locale.region_name(id)
14
+ end
15
+ end
16
+
17
+ attr_reader :id
18
+
19
+ def initialize(id)
20
+ @id = id
21
+ end
22
+
23
+ def name
24
+ Locale.region_name(id) || id
25
+ end
26
+
27
+ def zone
28
+ zone_id = REGION_PREFIX_TO_ZONE.find { |prefix, _| id.start_with?(prefix) }&.last
29
+
30
+ Zone.new(zone_id) if zone_id
31
+ end
32
+
33
+ def to_s = name
34
+ def inspect = "#<Zone::Region id=#{id.inspect} name=#{name.inspect}>"
35
+
36
+ def ==(other)
37
+ case other
38
+ when Region then id == other.id
39
+ when String then id == other
40
+ else false
41
+ end
42
+ end
43
+
44
+ alias eql? ==
45
+
46
+ def hash
47
+ id.hash
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hytale
4
+ module Client
5
+ module Zone
6
+ ZONE_TO_REGION_PREFIX = {
7
+ "Emerald_Wilds" => "Zone1",
8
+ "Howling_Sands" => "Zone2",
9
+ "Whisperfrost_Frontiers" => "Zone3",
10
+ "Devastated_Lands" => "Zone4",
11
+ "Oceans" => "Oceans",
12
+ }.freeze
13
+
14
+ REGION_PREFIX_TO_ZONE = ZONE_TO_REGION_PREFIX.invert.freeze
15
+
16
+ class << self
17
+ def all
18
+ Locale.zones.keys.map { |id| new(id) }
19
+ end
20
+
21
+ def find(id)
22
+ new(id) if Locale.zone_name(id)
23
+ end
24
+
25
+ def new(id)
26
+ Zone::Base.new(id)
27
+ end
28
+ end
29
+
30
+ class Base
31
+ attr_reader :id
32
+
33
+ def initialize(id)
34
+ @id = id
35
+ end
36
+
37
+ def name
38
+ Locale.zone_name(id) || id
39
+ end
40
+
41
+ def regions
42
+ prefix = ZONE_TO_REGION_PREFIX[id]
43
+
44
+ return [] unless prefix
45
+
46
+ Region.all.select { |region| region.id.start_with?(prefix) }
47
+ end
48
+
49
+ def to_s = name
50
+ def inspect = "#<Zone id=#{id.inspect} name=#{name.inspect}>"
51
+
52
+ def ==(other)
53
+ case other
54
+ when Base then id == other.id
55
+ when String then id == other
56
+ else false
57
+ end
58
+ end
59
+
60
+ alias eql? ==
61
+
62
+ def hash
63
+ id.hash
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,6 +3,9 @@
3
3
  module Hytale
4
4
  module Server
5
5
  class Process
6
+ def self.running?
7
+ false
8
+ end
6
9
  end
7
10
  end
8
11
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hytale
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hytale
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
@@ -123,6 +123,8 @@ files:
123
123
  - lib/hytale/client/world.rb
124
124
  - lib/hytale/client/world/client_effects.rb
125
125
  - lib/hytale/client/world/death_settings.rb
126
+ - lib/hytale/client/zone.rb
127
+ - lib/hytale/client/zone/region.rb
126
128
  - lib/hytale/server.rb
127
129
  - lib/hytale/server/process.rb
128
130
  - lib/hytale/version.rb