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
data/README.md
CHANGED
|
@@ -1,39 +1,1454 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>Hytale Ruby</h1>
|
|
3
|
+
<h4>Ruby gem for reading Hytale game data.</h4>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p>
|
|
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-ruby"></a>
|
|
8
|
+
</p>
|
|
4
9
|
|
|
5
|
-
|
|
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>
|
|
11
|
+
</div>
|
|
6
12
|
|
|
7
13
|
## Installation
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
**Add to your Gemfile:**
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
```ruby
|
|
18
|
+
gem "hytale"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Or install directly:**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gem install hytale
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## CLI
|
|
28
|
+
|
|
29
|
+
The gem includes a `hytale` command for quick access to game data.
|
|
30
|
+
|
|
31
|
+
**Show installation info:**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
hytale info
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**List all saves:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
hytale saves
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Show save details:**
|
|
12
44
|
|
|
13
45
|
```bash
|
|
14
|
-
|
|
46
|
+
hytale save "New World"
|
|
15
47
|
```
|
|
16
48
|
|
|
17
|
-
|
|
49
|
+
**Show player details:**
|
|
18
50
|
|
|
19
51
|
```bash
|
|
20
|
-
|
|
52
|
+
hytale player "New World" marcoroth
|
|
21
53
|
```
|
|
22
54
|
|
|
55
|
+
**Show game settings:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
hytale settings
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Show launcher log:**
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
hytale log
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**List prefabs:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
hytale prefabs
|
|
71
|
+
hytale prefabs Trees
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Available commands:**
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `info` | Show Hytale installation info |
|
|
79
|
+
| `settings` | Show game settings |
|
|
80
|
+
| `saves` | List all saves |
|
|
81
|
+
| `save <name>` | Show details for a specific save |
|
|
82
|
+
| `player <save> [name]` | Show player details |
|
|
83
|
+
| `map <save>` | Show map of explored regions |
|
|
84
|
+
| `prefabs [category]` | List prefabs by category |
|
|
85
|
+
| `log` | Show launcher log summary |
|
|
86
|
+
| `help` | Show help message |
|
|
87
|
+
|
|
23
88
|
## Usage
|
|
24
89
|
|
|
25
|
-
|
|
90
|
+
### Quick Start
|
|
26
91
|
|
|
27
|
-
|
|
92
|
+
```ruby
|
|
93
|
+
require "hytale"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Check if Hytale is installed:**
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
Hytale.client.installed? # => true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Read game settings:**
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
settings = Hytale.settings
|
|
106
|
+
settings.window_size # => [1280, 720]
|
|
107
|
+
settings.field_of_view # => 75
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**List all saves:**
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
Hytale.saves.each do |save|
|
|
114
|
+
puts "#{save.name}: #{save.world.game_mode}"
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Object Relationships
|
|
119
|
+
|
|
120
|
+
Understanding how the main objects relate to each other:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Save (a saved game folder)
|
|
124
|
+
├── World (configuration for a dimension)
|
|
125
|
+
│ └── Map (terrain data)
|
|
126
|
+
│ ├── Region (32x32 chunk file)
|
|
127
|
+
│ │ └── Chunk (16x16 block column)
|
|
128
|
+
│ └── Markers, Time, etc.
|
|
129
|
+
├── Players
|
|
130
|
+
├── Memories
|
|
131
|
+
└── Permissions, Bans, etc.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Object | Description |
|
|
135
|
+
|--------|-------------|
|
|
136
|
+
| **Save** | A saved game folder (e.g., "New World 1"). Contains worlds, players, permissions. |
|
|
137
|
+
| **World** | A dimension's configuration (`config.json`). Settings like seed, game mode, spawn point. |
|
|
138
|
+
| **Map** | The actual terrain data for a world. Contains regions with chunk data. |
|
|
139
|
+
| **Region** | A 32x32 chunk area stored in a `.region.bin` file. |
|
|
140
|
+
| **Chunk** | A 16x16 column of blocks. The smallest unit of terrain. |
|
|
141
|
+
|
|
142
|
+
**Navigating between objects:**
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
save = Hytale.saves.first
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Save -> Worlds**
|
|
149
|
+
```ruby
|
|
150
|
+
save.world_names # => ["default", "flat_world", ...]
|
|
151
|
+
save.worlds # => [World, World, ...]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Save -> World (specific)**
|
|
155
|
+
```ruby
|
|
156
|
+
world = save.world("flat_world")
|
|
157
|
+
world.display_name # => "Flat"
|
|
158
|
+
world.game_mode # => "Creative"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**World -> Map**
|
|
162
|
+
```ruby
|
|
163
|
+
map = world.map
|
|
164
|
+
map.regions.count # => 4
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Save -> Map (shortcut)**
|
|
168
|
+
```ruby
|
|
169
|
+
map = save.map("flat_world")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Save -> All Maps**
|
|
173
|
+
```ruby
|
|
174
|
+
save.maps.each do |map|
|
|
175
|
+
puts "#{map.world_name}: #{map.regions.count} regions"
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Map -> Regions -> Chunks**
|
|
180
|
+
```ruby
|
|
181
|
+
region = map.regions.first
|
|
182
|
+
|
|
183
|
+
region.each_chunk do |chunk|
|
|
184
|
+
puts "#{chunk.local_x}, #{chunk.local_z}: #{chunk.block_types.join(', ')}"
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Reading Settings
|
|
189
|
+
|
|
190
|
+
Settings provides access to all game configuration:
|
|
191
|
+
|
|
192
|
+
**Display:**
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
settings = Hytale.settings
|
|
196
|
+
|
|
197
|
+
settings.fullscreen? # => false
|
|
198
|
+
settings.window_size # => [1280, 720]
|
|
199
|
+
settings.vsync? # => true
|
|
200
|
+
settings.fps_limit # => 118
|
|
201
|
+
settings.field_of_view # => 75
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Rendering:**
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
settings.rendering.view_distance # => 384
|
|
208
|
+
settings.rendering.anti_aliasing # => 3
|
|
209
|
+
settings.rendering.shadows # => 2
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Audio:**
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
settings.audio.master_volume # => 0.85
|
|
216
|
+
settings.audio.music_volume # => 0.85
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Gameplay:**
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
settings.gameplay.arachnophobia_mode? # => false
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Settings modules:**
|
|
226
|
+
|
|
227
|
+
| Module | Description |
|
|
228
|
+
|--------|-------------|
|
|
229
|
+
| `rendering` | Graphics settings (view distance, shadows, AA, bloom) |
|
|
230
|
+
| `audio` | Volume levels and output device |
|
|
231
|
+
| `mouse_settings` | Sensitivity and inversion |
|
|
232
|
+
| `gameplay` | Game behavior options |
|
|
233
|
+
| `builder_tools` | Creative mode tool settings |
|
|
234
|
+
| `input_bindings` | Key bindings |
|
|
235
|
+
|
|
236
|
+
### Working with Saves
|
|
237
|
+
|
|
238
|
+
Access world saves and their contents:
|
|
239
|
+
|
|
240
|
+
**List all saves:**
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
saves = Hytale.saves
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Find a specific save:**
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
save = Hytale.client.save("New World")
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**World configuration:**
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
world = save.world
|
|
256
|
+
world.display_name # => "New World"
|
|
257
|
+
world.seed # => 1768313554213
|
|
258
|
+
world.game_mode # => "Adventure"
|
|
259
|
+
world.pvp_enabled? # => false
|
|
260
|
+
world.daytime_duration # => 1728
|
|
261
|
+
world.nighttime_duration # => 1151
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Death settings:**
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
world.death_settings.items_loss_percentage # => 50.0
|
|
268
|
+
world.death_settings.durability_loss_percentage # => 10.0
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Save contents:**
|
|
272
|
+
|
|
273
|
+
| Method | Description |
|
|
274
|
+
|--------|-------------|
|
|
275
|
+
| `world(name)` | World configuration (seed, game mode, day/night cycle) |
|
|
276
|
+
| `worlds` | All World objects in the save |
|
|
277
|
+
| `world_names` | List of world directory names |
|
|
278
|
+
| `map(name)` | Map data for a specific world |
|
|
279
|
+
| `maps` | All Map objects for all worlds |
|
|
280
|
+
| `players` | All players in this save |
|
|
281
|
+
| `memories` | Discovered NPCs/creatures |
|
|
282
|
+
| `permissions` | Server permissions and groups |
|
|
283
|
+
| `backups` | Automatic backup files |
|
|
284
|
+
| `logs` | Server log files |
|
|
285
|
+
| `mods` | Installed mods |
|
|
286
|
+
|
|
287
|
+
### Reading Player Data
|
|
288
|
+
|
|
289
|
+
Access player inventory, stats, and progress:
|
|
290
|
+
|
|
291
|
+
**Basic info:**
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
save = Hytale.client.save("New World")
|
|
295
|
+
player = save.players.first
|
|
296
|
+
|
|
297
|
+
player.name # => "marcoroth"
|
|
298
|
+
player.uuid # => "00000000-0000-0000-0000-000000000000"
|
|
299
|
+
player.position # => (590.75, 123.0, 374.2)
|
|
300
|
+
player.game_mode # => "Adventure"
|
|
301
|
+
player.discovered_zones # => [Zone::Region, Zone::Region, ...]
|
|
302
|
+
player.skin # => PlayerSkin object
|
|
303
|
+
player.avatar_preview_path # => "/path/to/CachedAvatarPreviews/uuid.png"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Stats:**
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
player.stats.health # => 96.0
|
|
310
|
+
player.stats.stamina # => 9.3
|
|
311
|
+
player.stats.oxygen # => 100.0
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Inventory:**
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
player.inventory.hotbar.items.each do |item|
|
|
318
|
+
puts "#{item.name} - #{item.durability_percent}%"
|
|
319
|
+
end
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Armor:**
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
player.inventory.armor.items.each do |item|
|
|
326
|
+
puts item.name
|
|
327
|
+
end
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Player inventory slots:**
|
|
331
|
+
|
|
332
|
+
| Slot | Description |
|
|
333
|
+
|------|-------------|
|
|
334
|
+
| `hotbar` | 9-slot quick access bar |
|
|
335
|
+
| `storage` | Main inventory (36 slots) |
|
|
336
|
+
| `backpack` | Optional backpack storage (if equipped) |
|
|
337
|
+
| `armor` | Head, chest, hands, legs |
|
|
338
|
+
| `utility` | Utility items (4 slots) |
|
|
339
|
+
| `tools` | Builder/editor tools |
|
|
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
|
+
|
|
354
|
+
**Item properties:**
|
|
355
|
+
|
|
356
|
+
```ruby
|
|
357
|
+
item.id # => "Tool_Pickaxe_Copper"
|
|
358
|
+
item.name # => "Tool Pickaxe Copper"
|
|
359
|
+
item.quantity # => 1
|
|
360
|
+
item.durability # => 29.75
|
|
361
|
+
item.max_durability # => 200.0
|
|
362
|
+
item.durability_percent # => 14.9
|
|
363
|
+
item.damaged? # => true
|
|
364
|
+
```
|
|
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
|
+
|
|
439
|
+
### Memories (Discovered Creatures)
|
|
440
|
+
|
|
441
|
+
Track discovered NPCs and creatures:
|
|
442
|
+
|
|
443
|
+
**Count and list:**
|
|
444
|
+
|
|
445
|
+
```ruby
|
|
446
|
+
memories = save.memories
|
|
447
|
+
memories.count # => 42
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**List all discovered roles:**
|
|
451
|
+
|
|
452
|
+
```ruby
|
|
453
|
+
memories.roles
|
|
454
|
+
# => ["Bat", "Bear_Grizzly", "Bluebird", "Boar", ...]
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**List discovery locations:**
|
|
458
|
+
|
|
459
|
+
```ruby
|
|
460
|
+
memories.locations
|
|
461
|
+
# => ["ForgottenTemple", "Zone1_Tier1", "Zone1_Tier2", ...]
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Find specific creatures:**
|
|
465
|
+
|
|
466
|
+
```ruby
|
|
467
|
+
memories.find_by_role("Wolf_Black")
|
|
468
|
+
# => Memory: Wolf Black found at Zone1_Tier1
|
|
469
|
+
|
|
470
|
+
memories.find_all_by_location("ForgottenTemple")
|
|
471
|
+
# => [Memory: Duck, Memory: Kweebec_Rootling, ...]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Iterate:**
|
|
475
|
+
|
|
476
|
+
```ruby
|
|
477
|
+
memories.each do |memory|
|
|
478
|
+
puts "#{memory.friendly_name} at #{memory.location} (#{memory.captured_at})"
|
|
479
|
+
end
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Permissions
|
|
483
|
+
|
|
484
|
+
Read server permissions and groups:
|
|
485
|
+
|
|
486
|
+
**List groups:**
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
permissions = save.permissions
|
|
490
|
+
|
|
491
|
+
permissions.groups
|
|
492
|
+
# => {"Default" => [], "OP" => ["*"]}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Check user permissions:**
|
|
496
|
+
|
|
497
|
+
```ruby
|
|
498
|
+
permissions.user_groups(uuid)
|
|
499
|
+
# => ["Adventure"]
|
|
500
|
+
|
|
501
|
+
permissions.op?(uuid)
|
|
502
|
+
# => false
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Map Data
|
|
506
|
+
|
|
507
|
+
Access explored regions and map markers:
|
|
508
|
+
|
|
509
|
+
**Get map for a save:**
|
|
510
|
+
|
|
511
|
+
```ruby
|
|
512
|
+
map = save.map
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Region coverage:**
|
|
516
|
+
|
|
517
|
+
```ruby
|
|
518
|
+
map.regions.count # => 10
|
|
519
|
+
map.total_size_mb # => 120.56
|
|
520
|
+
map.bounds # => {min_x: -1, max_x: 2, min_z: -1, max_z: 1, ...}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Individual regions:**
|
|
524
|
+
|
|
525
|
+
```ruby
|
|
526
|
+
map.regions.each do |region|
|
|
527
|
+
puts "#{region.x}, #{region.z}: #{region.size_mb} MB"
|
|
528
|
+
end
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Region details:**
|
|
532
|
+
|
|
533
|
+
```ruby
|
|
534
|
+
region = map.regions.first
|
|
535
|
+
region.header # => {version: 1, chunk_count: 1024, ...}
|
|
536
|
+
region.chunk_count # => 207 (non-empty chunks)
|
|
537
|
+
region.block_types # => ["Rock_Stone", "Soil_Grass", ...]
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Block types across all regions:**
|
|
541
|
+
|
|
542
|
+
```ruby
|
|
543
|
+
map.block_types # => ["Ore_Copper", "Plant_Bush", "Rock_Stone", ...]
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Map markers (discovered locations):**
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
map.markers.each do |marker|
|
|
550
|
+
puts "#{marker.name} at #{marker.position}"
|
|
551
|
+
end
|
|
552
|
+
# => "Forgotten Temple Portal Enter at (832, 113, 367)"
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**ASCII map of explored regions:**
|
|
556
|
+
|
|
557
|
+
```ruby
|
|
558
|
+
puts map.to_ascii(players: save.players)
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
-1 0 1 2
|
|
563
|
+
--------------
|
|
564
|
+
-1 | o O O |
|
|
565
|
+
0 | O # M . |
|
|
566
|
+
1 | o O o |
|
|
567
|
+
--------------
|
|
568
|
+
|
|
569
|
+
Legend: . = small, o = medium, O = large, # = huge, Letter = player
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Global Coordinates
|
|
573
|
+
|
|
574
|
+
Access regions, chunks, and blocks using world coordinates:
|
|
575
|
+
|
|
576
|
+
```ruby
|
|
577
|
+
map = save.map
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Get region containing world coordinates:**
|
|
581
|
+
|
|
582
|
+
```ruby
|
|
583
|
+
region = map.region_at_world(100, 200)
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Get chunk at world coordinates:**
|
|
587
|
+
|
|
588
|
+
```ruby
|
|
589
|
+
chunk = map.chunk_at(100, 200)
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
**Get block at world coordinates:**
|
|
593
|
+
|
|
594
|
+
```ruby
|
|
595
|
+
block = map.block_at(100, 50, 200)
|
|
596
|
+
block.id # => "Rock_Stone"
|
|
597
|
+
block.world_position # => [100, 50, 200]
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Coordinate conversion helpers:**
|
|
601
|
+
|
|
602
|
+
```ruby
|
|
603
|
+
map.world_to_region_coords(100, 200) # => [0, 0]
|
|
604
|
+
map.world_to_region_coords(-100, -200) # => [-1, -1]
|
|
605
|
+
map.world_to_chunk_local_coords(100, 200) # => [6, 12]
|
|
606
|
+
map.world_to_block_local_coords(100, 200) # => [4, 8]
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Coordinate system:**
|
|
610
|
+
|
|
611
|
+
| Unit | Size | Description |
|
|
612
|
+
|------|------|-------------|
|
|
613
|
+
| Block | 1 | Smallest unit |
|
|
614
|
+
| Chunk | 16×16 blocks | Vertical column of blocks |
|
|
615
|
+
| Region | 32×32 chunks (512×512 blocks) | Stored in `.region.bin` files |
|
|
616
|
+
|
|
617
|
+
Region 0 covers blocks 0..511, region -1 covers -512..-1, etc.
|
|
618
|
+
|
|
619
|
+
### Map Rendering
|
|
620
|
+
|
|
621
|
+
Generate PNG images of maps using colors derived from block textures.
|
|
622
|
+
|
|
623
|
+
**Render modes:**
|
|
624
|
+
|
|
625
|
+
| Mode | Description | Speed |
|
|
626
|
+
|------|-------------|-------|
|
|
627
|
+
| Fast (`detailed: false`) | Colors entire chunk with dominant surface block | ~5s for 10 regions |
|
|
628
|
+
| Detailed (`detailed: true`) | Renders each block individually using `surface_at` | ~15s for 10 regions |
|
|
629
|
+
|
|
630
|
+
**Render a map to PNG:**
|
|
631
|
+
|
|
632
|
+
```ruby
|
|
633
|
+
save = Hytale.saves.first
|
|
634
|
+
map = save.map
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Fast mode (default):** uniform color per chunk:
|
|
638
|
+
|
|
639
|
+
```ruby
|
|
640
|
+
map.render_to_png("/tmp/map_fast.png")
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Detailed mode:** per-block accuracy:
|
|
644
|
+
|
|
645
|
+
```ruby
|
|
646
|
+
map.render_to_png("/tmp/map_detailed.png", detailed: true)
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**With scale (2x = 2 pixels per block):**
|
|
650
|
+
|
|
651
|
+
```ruby
|
|
652
|
+
map.render_to_png("/tmp/map_2x.png", scale: 2, detailed: true)
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
**Render a single region:**
|
|
656
|
+
|
|
657
|
+
```ruby
|
|
658
|
+
region = map.regions.first
|
|
659
|
+
region.render_to_png("/tmp/region.png", scale: 2, detailed: true)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
**Render a single chunk:**
|
|
663
|
+
|
|
664
|
+
```ruby
|
|
665
|
+
chunk = region.chunks.values.first
|
|
666
|
+
chunk.render_to_png("/tmp/chunk.png", scale: 4, detailed: true)
|
|
667
|
+
```
|
|
28
668
|
|
|
29
|
-
|
|
669
|
+
**Using the Renderer directly:**
|
|
30
670
|
|
|
31
|
-
|
|
671
|
+
```ruby
|
|
672
|
+
renderer = Hytale::Client::Map::Renderer.new
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
**Get average color from a block's texture:**
|
|
676
|
+
|
|
677
|
+
```ruby
|
|
678
|
+
renderer.block_color("Soil_Grass") # => ChunkyPNG color
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
**Render with custom options:**
|
|
682
|
+
|
|
683
|
+
```ruby
|
|
684
|
+
png = renderer.render_map(map, scale: 2, detailed: true)
|
|
685
|
+
png.save("/tmp/map.png")
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**Check cached colors:**
|
|
689
|
+
|
|
690
|
+
```ruby
|
|
691
|
+
renderer.color_cache
|
|
692
|
+
# => {"Soil_Grass" => 7379500, "Rock_Stone" => 7894873, ...}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**Color extraction:**
|
|
696
|
+
|
|
697
|
+
The renderer extracts average colors from block textures:
|
|
698
|
+
|
|
699
|
+
| Block Type | Color | Source Texture |
|
|
700
|
+
|------------|-------|----------------|
|
|
701
|
+
| Soil_Grass | `#709C2C` | Soil_Grass_Sunny.png |
|
|
702
|
+
| Soil_Dirt | `#8A652C` | Soil_Dirt.png |
|
|
703
|
+
| Rock_Stone | `#787759` | Rock_Stone.png |
|
|
704
|
+
| Soil_Sand | `#CFA643` | Soil_Sand.png |
|
|
705
|
+
|
|
706
|
+
Blocks without textures use sensible defaults (e.g., blue for water).
|
|
707
|
+
|
|
708
|
+
**Chunk analysis:**
|
|
709
|
+
|
|
710
|
+
```ruby
|
|
711
|
+
chunk = region.each_chunk.first
|
|
712
|
+
|
|
713
|
+
chunk.block_types # => ["Rock_Stone", "Soil_Dirt", "Soil_Grass", ...]
|
|
714
|
+
chunk.terrain_type # => :grassland
|
|
715
|
+
chunk.water? # => false
|
|
716
|
+
chunk.vegetation? # => true
|
|
717
|
+
chunk.local_x # => 15
|
|
718
|
+
chunk.local_z # => 8
|
|
719
|
+
chunk.world_x # => -272
|
|
720
|
+
chunk.world_z # => -408
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
**ASCII representation:**
|
|
724
|
+
|
|
725
|
+
```ruby
|
|
726
|
+
puts chunk.to_ascii_map
|
|
727
|
+
# GGGGGGGGGGGGGGGG
|
|
728
|
+
# GGGGGGGGGGGGGGGG
|
|
729
|
+
# ...
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Backups
|
|
733
|
+
|
|
734
|
+
Access automatic backup files:
|
|
735
|
+
|
|
736
|
+
```ruby
|
|
737
|
+
save.backups.each do |backup|
|
|
738
|
+
puts "#{backup.filename} - #{backup.size_mb} MB"
|
|
739
|
+
puts "Created: #{backup.created_at}"
|
|
740
|
+
end
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Launcher Log
|
|
744
|
+
|
|
745
|
+
Parse the Hytale launcher log:
|
|
746
|
+
|
|
747
|
+
**Current state:**
|
|
748
|
+
|
|
749
|
+
```ruby
|
|
750
|
+
log = Hytale.launcher_log
|
|
751
|
+
|
|
752
|
+
log.current_version # => "2026.01.13-b6c7e88"
|
|
753
|
+
log.current_channel # => "release"
|
|
754
|
+
log.current_profile_uuid # => "79816d74-..."
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
**Game launches:**
|
|
758
|
+
|
|
759
|
+
```ruby
|
|
760
|
+
log.game_launches.count # => 6
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
**Errors:**
|
|
764
|
+
|
|
765
|
+
```ruby
|
|
766
|
+
log.errors.each do |entry|
|
|
767
|
+
puts "[#{entry.timestamp}] #{entry.message}"
|
|
768
|
+
end
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
**Sessions:**
|
|
772
|
+
|
|
773
|
+
```ruby
|
|
774
|
+
log.sessions.each do |session|
|
|
775
|
+
puts "#{session.started_at} - v#{session.version}"
|
|
776
|
+
puts " Game launched: #{session.game_launched?}"
|
|
777
|
+
puts " Errors: #{session.errors.count}"
|
|
778
|
+
end
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
**Log entry types:**
|
|
782
|
+
|
|
783
|
+
| Method | Description |
|
|
784
|
+
|--------|-------------|
|
|
785
|
+
| `entries` | All log entries |
|
|
786
|
+
| `errors` | Error-level entries |
|
|
787
|
+
| `warnings` | Warning-level entries |
|
|
788
|
+
| `info` | Info-level entries |
|
|
789
|
+
| `game_launches` | Game start events |
|
|
790
|
+
| `updates` | Update events |
|
|
791
|
+
| `sessions` | Grouped by launcher start |
|
|
792
|
+
|
|
793
|
+
### Custom Data Path
|
|
794
|
+
|
|
795
|
+
Override the default data path:
|
|
796
|
+
|
|
797
|
+
**Set custom path:**
|
|
798
|
+
|
|
799
|
+
```ruby
|
|
800
|
+
Hytale::Client.data_path = "/path/to/hytale/data"
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**Reset to platform default:**
|
|
804
|
+
|
|
805
|
+
```ruby
|
|
806
|
+
Hytale::Client::Config.reset!
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Platform Support
|
|
810
|
+
|
|
811
|
+
The gem automatically detects the Hytale data directory:
|
|
812
|
+
|
|
813
|
+
| Platform | Default Path |
|
|
814
|
+
|----------|-------------|
|
|
815
|
+
| macOS | `~/Library/Application Support/Hytale` |
|
|
816
|
+
| Windows | `%APPDATA%/Hytale` |
|
|
817
|
+
| Linux | `~/.local/share/Hytale` |
|
|
818
|
+
|
|
819
|
+
### Process Detection
|
|
820
|
+
|
|
821
|
+
Detect if the Hytale client is running:
|
|
822
|
+
|
|
823
|
+
**Check if game is running:**
|
|
824
|
+
|
|
825
|
+
```ruby
|
|
826
|
+
Hytale.client.running?
|
|
827
|
+
# => true
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
**List running client processes:**
|
|
831
|
+
|
|
832
|
+
```ruby
|
|
833
|
+
Hytale::Client::Process.list
|
|
834
|
+
# => [#<Hytale::Client::Process pid=12345>]
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**Check a specific process:**
|
|
838
|
+
|
|
839
|
+
```ruby
|
|
840
|
+
process = Hytale::Client::Process.list.first
|
|
841
|
+
process.running? # => true
|
|
842
|
+
process.pid # => 12345
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
## API Reference
|
|
846
|
+
|
|
847
|
+
### Hytale (Top-level)
|
|
848
|
+
|
|
849
|
+
| Method | Description |
|
|
850
|
+
|--------|-------------|
|
|
851
|
+
| `Hytale.client` | Access client module |
|
|
852
|
+
| `Hytale.server` | Access server module |
|
|
853
|
+
| `Hytale.settings` | Load game settings |
|
|
854
|
+
| `Hytale.saves` | List all saves |
|
|
855
|
+
| `Hytale.players` | List all players across all saves |
|
|
856
|
+
| `Hytale.launcher_log` | Load launcher log |
|
|
857
|
+
|
|
858
|
+
### Hytale::Client
|
|
859
|
+
|
|
860
|
+
| Method | Description |
|
|
861
|
+
|--------|-------------|
|
|
862
|
+
| `installed?` | Check if Hytale is installed |
|
|
863
|
+
| `running?` | Check if Hytale client is running |
|
|
864
|
+
| `processes` | List running client processes |
|
|
865
|
+
| `data_path` | Get/set data directory |
|
|
866
|
+
| `settings` | Load Settings |
|
|
867
|
+
| `saves` | List all Save objects |
|
|
868
|
+
| `save(name)` | Find Save by name |
|
|
869
|
+
| `launcher_log` | Load LauncherLog |
|
|
870
|
+
| `prefabs` | List all Prefab objects |
|
|
871
|
+
| `prefab(name)` | Find Prefab by name |
|
|
872
|
+
| `prefab_categories` | List prefab category names |
|
|
873
|
+
| `prefabs_in_category(name)` | List prefabs in a category |
|
|
874
|
+
| `block_types` | List all BlockType objects |
|
|
875
|
+
| `block_type(id)` | Create BlockType by ID |
|
|
876
|
+
| `block_type_categories` | List block category names |
|
|
877
|
+
| `block_types_in_category(name)` | List block types in a category |
|
|
878
|
+
| `players` | List all players across all saves |
|
|
879
|
+
| `player(uuid)` | Find Player by UUID |
|
|
880
|
+
| `player_skins` | List all cached PlayerSkin objects |
|
|
881
|
+
| `player_skin(uuid)` | Find PlayerSkin by UUID |
|
|
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
|
+
|
|
911
|
+
### Hytale::Client::Map
|
|
912
|
+
|
|
913
|
+
| Method | Description |
|
|
914
|
+
|--------|-------------|
|
|
915
|
+
| `regions` | All Region objects |
|
|
916
|
+
| `region_at(x, z)` | Find region by region coordinates |
|
|
917
|
+
| `region_at_world(x, z)` | Find region by world coordinates |
|
|
918
|
+
| `chunk_at(x, z)` | Get chunk at world coordinates |
|
|
919
|
+
| `block_at(x, y, z)` | Get Block at world coordinates |
|
|
920
|
+
| `world_to_region_coords(x, z)` | Convert world → region coords |
|
|
921
|
+
| `world_to_chunk_local_coords(x, z)` | Convert world → chunk-local coords |
|
|
922
|
+
| `world_to_block_local_coords(x, z)` | Convert world → block-local coords |
|
|
923
|
+
| `bounds` | Map boundaries (min/max x/z) |
|
|
924
|
+
| `markers` | Map markers (discovered locations) |
|
|
925
|
+
| `block_types` | All block types across regions |
|
|
926
|
+
| `total_size_mb` | Total size of all region files |
|
|
927
|
+
| `render_to_png(path, scale:, detailed:)` | Render map to PNG image |
|
|
928
|
+
| `to_ascii(players:)` | ASCII representation |
|
|
929
|
+
|
|
930
|
+
### Hytale::Client::Map::Region
|
|
931
|
+
|
|
932
|
+
| Method | Description |
|
|
933
|
+
|--------|-------------|
|
|
934
|
+
| `x`, `z` | Region coordinates |
|
|
935
|
+
| `chunk_count` | Number of non-empty chunks |
|
|
936
|
+
| `chunk_exists?(x, z)` | Check if chunk exists at local coords |
|
|
937
|
+
| `chunk_at_index(idx)` | Get chunk by index (0-1023) |
|
|
938
|
+
| `each_chunk` | Iterate over all chunks |
|
|
939
|
+
| `block_types` | All block types in region |
|
|
940
|
+
| `render_to_png(path, scale:, detailed:)` | Render region to PNG image |
|
|
941
|
+
|
|
942
|
+
### Hytale::Client::Map::Chunk
|
|
943
|
+
|
|
944
|
+
| Method | Description |
|
|
945
|
+
|--------|-------------|
|
|
946
|
+
| `index` | Chunk index in region (0-1023) |
|
|
947
|
+
| `local_x`, `local_z` | Position within region (0-31) |
|
|
948
|
+
| `world_x`, `world_z` | World coordinates |
|
|
949
|
+
| `size` | Decompressed data size in bytes |
|
|
950
|
+
| `height` | Number of Y layers in chunk |
|
|
951
|
+
| `block_at(x, y, z)` | Get Block instance at local coordinates |
|
|
952
|
+
| `block_type_at(x, y, z)` | Get block type ID string (faster) |
|
|
953
|
+
| `surface_at(x, z)` | Find highest non-empty Block at X, Z |
|
|
954
|
+
| `block_types` | Block type IDs found in chunk |
|
|
955
|
+
| `block_palette` | Parsed palette (index → block name) |
|
|
956
|
+
| `terrain_type` | Detected terrain (`:grassland`, `:water`, etc.) |
|
|
957
|
+
| `water?` | Contains water blocks |
|
|
958
|
+
| `vegetation?` | Contains plant/grass blocks |
|
|
959
|
+
| `to_ascii_map` | 16x16 ASCII representation |
|
|
960
|
+
|
|
961
|
+
### Hytale::Client::Map::Block
|
|
962
|
+
|
|
963
|
+
| Method | Description |
|
|
964
|
+
|--------|-------------|
|
|
965
|
+
| `id` | Block type ID (e.g., "Rock_Stone") |
|
|
966
|
+
| `name` | Human-readable name |
|
|
967
|
+
| `category` | Block category (e.g., "Rock") |
|
|
968
|
+
| `block_type` | Associated BlockType instance |
|
|
969
|
+
| `x`, `y`, `z` | Local coordinates within chunk |
|
|
970
|
+
| `world_x`, `world_y`, `world_z` | World coordinates |
|
|
971
|
+
| `local_position` | `[x, y, z]` array |
|
|
972
|
+
| `world_position` | `[world_x, world_y, world_z]` array |
|
|
973
|
+
| `chunk` | Parent Chunk reference |
|
|
974
|
+
| `empty?` | Is air/empty block? |
|
|
975
|
+
| `solid?` | Is solid (not empty, not liquid)? |
|
|
976
|
+
| `liquid?` | Is water/lava? |
|
|
977
|
+
| `vegetation?` | Is plant/grass? |
|
|
978
|
+
| `texture_path` | Path to texture file |
|
|
979
|
+
| `texture_exists?` | Does texture exist? |
|
|
980
|
+
| `texture_data` | Raw PNG texture data |
|
|
981
|
+
|
|
982
|
+
### Hytale::Client::BlockType
|
|
983
|
+
|
|
984
|
+
| Method | Description |
|
|
985
|
+
|--------|-------------|
|
|
986
|
+
| `id` | Block type ID (e.g., "Rock_Stone") |
|
|
987
|
+
| `name` | Human-readable name |
|
|
988
|
+
| `category` | Block category (e.g., "Rock") |
|
|
989
|
+
| `subcategory` | Block subcategory if available |
|
|
990
|
+
| `texture_name` | Texture filename |
|
|
991
|
+
| `texture_path` | Path to texture file |
|
|
992
|
+
| `texture_exists?` | Does texture exist? |
|
|
993
|
+
| `texture_data` | Raw PNG texture data |
|
|
994
|
+
| `all_textures` | (class method) List all texture names |
|
|
995
|
+
|
|
996
|
+
### Hytale::Client::Map::Renderer
|
|
997
|
+
|
|
998
|
+
| Method | Description |
|
|
999
|
+
|--------|-------------|
|
|
1000
|
+
| `block_color(type)` | Get average color for block type |
|
|
1001
|
+
| `render_chunk(chunk, scale:, detailed:)` | Render chunk to ChunkyPNG image |
|
|
1002
|
+
| `render_region(region, scale:, detailed:)` | Render region to ChunkyPNG image |
|
|
1003
|
+
| `render_map(map, scale:, detailed:)` | Render map to ChunkyPNG image |
|
|
1004
|
+
| `save_region(region, path, scale:, detailed:)` | Save region PNG to file |
|
|
1005
|
+
| `save_map(map, path, scale:, detailed:)` | Save map PNG to file |
|
|
1006
|
+
| `color_cache` | Hash of cached block colors |
|
|
1007
|
+
|
|
1008
|
+
## Technical Details
|
|
1009
|
+
|
|
1010
|
+
### Region File Format (`.region.bin`)
|
|
1011
|
+
|
|
1012
|
+
Region files use the `HytaleIndexedStorage` format:
|
|
1013
|
+
|
|
1014
|
+
**Header (32 bytes):**
|
|
1015
|
+
|
|
1016
|
+
| Offset | Size | Description |
|
|
1017
|
+
|--------|------|-------------|
|
|
1018
|
+
| 0 | 20 | Magic: "HytaleIndexedStorage" |
|
|
1019
|
+
| 20 | 4 | Version (BE) = 1 |
|
|
1020
|
+
| 24 | 4 | Chunk count (BE) = 1024 |
|
|
1021
|
+
| 28 | 4 | Index table size (BE) = 4096 |
|
|
1022
|
+
|
|
1023
|
+
**Index Table (4096 bytes):**
|
|
1024
|
+
|
|
1025
|
+
- 1024 entries of 4 bytes each (big-endian)
|
|
1026
|
+
- Non-zero value indicates chunk exists
|
|
1027
|
+
|
|
1028
|
+
**Data Section:**
|
|
1029
|
+
|
|
1030
|
+
- Chunks stored at 4096-byte aligned positions
|
|
1031
|
+
- Each chunk: `[decompressed_size 4B BE] [compressed_size 4B BE] [ZSTD data]`
|
|
1032
|
+
- ZSTD magic: `0x28B52FFD`
|
|
1033
|
+
|
|
1034
|
+
**Decompression:**
|
|
1035
|
+
|
|
1036
|
+
```ruby
|
|
1037
|
+
require "zstd-ruby"
|
|
1038
|
+
region = Hytale::Client::Map::Region.new(path)
|
|
1039
|
+
region.block_types # Extracts block palette from chunks
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
### Chunk Data Format
|
|
1043
|
+
|
|
1044
|
+
Decompressed chunk data uses a BSON-like structure with numbered sections (0-9) containing block data.
|
|
1045
|
+
|
|
1046
|
+
**Block Data Section:**
|
|
1047
|
+
|
|
1048
|
+
| Offset | Size | Description |
|
|
1049
|
+
|--------|------|-------------|
|
|
1050
|
+
| 0 | 3 | Zeros (padding) |
|
|
1051
|
+
| 3 | 1 | Type marker (0x0A) |
|
|
1052
|
+
| 4 | 1 | Version (0x01) |
|
|
1053
|
+
| 5 | 1 | Zero |
|
|
1054
|
+
| 6 | 1 | Palette count |
|
|
1055
|
+
| 7 | 2 | Zeros |
|
|
1056
|
+
| 9 | N | Palette entries |
|
|
1057
|
+
| 9+N | M | Block data (4-bit packed) |
|
|
1058
|
+
|
|
1059
|
+
**Palette Entry Format:**
|
|
1060
|
+
|
|
1061
|
+
| Size | Description |
|
|
1062
|
+
|------|-------------|
|
|
1063
|
+
| 1 | String length |
|
|
1064
|
+
| N | Block name (e.g., "Rock_Stone") |
|
|
1065
|
+
| 4 | Metadata (palette index at byte 2) |
|
|
1066
|
+
|
|
1067
|
+
**Block Data Encoding:**
|
|
1068
|
+
|
|
1069
|
+
- 4-bit packed indices (2 blocks per byte)
|
|
1070
|
+
- 128 bytes per Y layer (16×16 blocks)
|
|
1071
|
+
- Low nibble = block at even position
|
|
1072
|
+
- High nibble = block at odd position
|
|
1073
|
+
|
|
1074
|
+
**Accessing blocks:**
|
|
1075
|
+
|
|
1076
|
+
**Block at (x, y, z) within chunk:**
|
|
1077
|
+
|
|
1078
|
+
```ruby
|
|
1079
|
+
layer_offset = y * 128
|
|
1080
|
+
block_index = z * 16 + x
|
|
1081
|
+
byte_offset = layer_offset + (block_index / 2)
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
**Extract 4-bit index:**
|
|
1085
|
+
|
|
1086
|
+
```ruby
|
|
1087
|
+
if block_index.even?
|
|
1088
|
+
palette_index = byte & 0x0F # Low nibble
|
|
1089
|
+
else
|
|
1090
|
+
palette_index = (byte >> 4) & 0x0F # High nibble
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
block_name = palette[palette_index]
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
### Prefab File Format (`.prefab.json.lpf`)
|
|
1097
|
+
|
|
1098
|
+
Prefab files store pre-built structures (trees, buildings, dungeons, etc.) in a custom binary format. Despite the `.json` in the filename, these are binary files, not JSON.
|
|
1099
|
+
|
|
1100
|
+
**Header (21 bytes):**
|
|
1101
|
+
|
|
1102
|
+
| Offset | Size | Description |
|
|
1103
|
+
|--------|------|-------------|
|
|
1104
|
+
| 0 | 2 | Palette offset (BE) = 21 |
|
|
1105
|
+
| 2 | 2 | Header value (BE) |
|
|
1106
|
+
| 4 | 10 | Reserved/dimensions |
|
|
1107
|
+
| 14 | 2 | Palette count (BE) |
|
|
1108
|
+
| 16 | 5 | Reserved |
|
|
1109
|
+
|
|
1110
|
+
**Block Palette:**
|
|
1111
|
+
|
|
1112
|
+
Each palette entry:
|
|
1113
|
+
|
|
1114
|
+
| Size | Description |
|
|
1115
|
+
|------|-------------|
|
|
1116
|
+
| 1 | String length |
|
|
1117
|
+
| N | Block name (ASCII) |
|
|
1118
|
+
| 2 | Flags (BE) |
|
|
1119
|
+
| 2 | Block ID (BE) |
|
|
1120
|
+
| 1 | Extra data (rotation/state) |
|
|
1121
|
+
|
|
1122
|
+
**Placement Data:**
|
|
1123
|
+
|
|
1124
|
+
Block placement coordinates follow the palette. Format varies by prefab complexity.
|
|
1125
|
+
|
|
1126
|
+
**List all prefabs:**
|
|
1127
|
+
|
|
1128
|
+
```ruby
|
|
1129
|
+
Hytale.client.prefabs.each do |prefab|
|
|
1130
|
+
puts "#{prefab.name}: #{prefab.palette.size} block types"
|
|
1131
|
+
end
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
**Get prefab categories:**
|
|
1135
|
+
|
|
1136
|
+
```ruby
|
|
1137
|
+
Hytale.client.prefab_categories
|
|
1138
|
+
# => ["Cave", "Dungeon", "Mineshaft", "Monuments", "Npc", "Plants", ...]
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
**Find prefabs by category:**
|
|
1142
|
+
|
|
1143
|
+
```ruby
|
|
1144
|
+
Hytale.client.prefabs_in_category("Trees")
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**Find specific prefab:**
|
|
1148
|
+
|
|
1149
|
+
```ruby
|
|
1150
|
+
prefab = Hytale.client.prefab("Burnt_dead_Stage2_005")
|
|
1151
|
+
prefab.name # => "Burnt_dead_Stage2_005"
|
|
1152
|
+
prefab.category # => "Trees"
|
|
1153
|
+
prefab.block_names # => ["Wood_Burnt_Branch_Long", "Wood_Burnt_Trunk", ...]
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
**Access palette entries:**
|
|
1157
|
+
|
|
1158
|
+
```ruby
|
|
1159
|
+
prefab.palette.each do |entry|
|
|
1160
|
+
puts "#{entry.name} (ID: 0x#{format('%04X', entry.block_id)})"
|
|
1161
|
+
end
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
### Blocks and Block Types
|
|
1165
|
+
|
|
1166
|
+
The gem distinguishes between:
|
|
1167
|
+
- **BlockType** - A block definition (e.g., "Rock_Stone") with texture and category info
|
|
1168
|
+
- **Block** - A specific block at coordinates in the world, referencing its BlockType
|
|
1169
|
+
|
|
1170
|
+
**BlockType - Block definitions:**
|
|
1171
|
+
|
|
1172
|
+
```ruby
|
|
1173
|
+
Hytale.client.block_types.count # => 1156
|
|
1174
|
+
Hytale.client.block_type_categories # => ["Alchemy", "Bench", "Ore", "Plant", "Rock", ...]
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
**Get block types by category:**
|
|
1178
|
+
|
|
1179
|
+
```ruby
|
|
1180
|
+
Hytale.client.block_types_in_category("Ore")
|
|
1181
|
+
# => [BlockType: Ore_Copper_Stone, BlockType: Ore_Iron_Stone, ...]
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**Create a block type directly:**
|
|
1185
|
+
|
|
1186
|
+
```ruby
|
|
1187
|
+
block_type = Hytale.client.block_type("Rock_Stone")
|
|
1188
|
+
block_type.id # => "Rock_Stone"
|
|
1189
|
+
block_type.name # => "Rock Stone"
|
|
1190
|
+
block_type.category # => "Rock"
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
**BlockType textures:**
|
|
1194
|
+
|
|
1195
|
+
```ruby
|
|
1196
|
+
block_type.texture_path # => "/path/to/gem/assets/Common/BlockTextures/Rock_Stone.png"
|
|
1197
|
+
block_type.texture_exists? # => true
|
|
1198
|
+
block_type.texture_data # => PNG binary data
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
**List all available textures:**
|
|
1202
|
+
|
|
1203
|
+
```ruby
|
|
1204
|
+
Hytale::Client::BlockType.all_textures
|
|
1205
|
+
# => ["Bone_Side", "Bone_Top", "Calcite", ...]
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
**Block - Positioned blocks in the world:**
|
|
1209
|
+
|
|
1210
|
+
```ruby
|
|
1211
|
+
chunk = region.chunks.values.first
|
|
1212
|
+
```
|
|
1213
|
+
|
|
1214
|
+
**Get a block at specific coordinates:**
|
|
1215
|
+
|
|
1216
|
+
```ruby
|
|
1217
|
+
block = chunk.block_at(8, 50, 8)
|
|
1218
|
+
block.id # => "Rock_Quartzite"
|
|
1219
|
+
block.name # => "Rock Quartzite"
|
|
1220
|
+
block.category # => "Rock"
|
|
1221
|
+
block.block_type # => BlockType instance
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
**Local position within chunk:**
|
|
1225
|
+
|
|
1226
|
+
```ruby
|
|
1227
|
+
block.x # => 8
|
|
1228
|
+
block.y # => 50
|
|
1229
|
+
block.z # => 8
|
|
1230
|
+
block.local_position # => [8, 50, 8]
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
**World coordinates:**
|
|
1234
|
+
|
|
1235
|
+
```ruby
|
|
1236
|
+
block.world_x # => -8
|
|
1237
|
+
block.world_y # => 50
|
|
1238
|
+
block.world_z # => -280
|
|
1239
|
+
block.world_position # => [-8, 50, -280]
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
**Block properties:**
|
|
1243
|
+
|
|
1244
|
+
```ruby
|
|
1245
|
+
block.empty? # => false
|
|
1246
|
+
block.solid? # => true
|
|
1247
|
+
block.liquid? # => false
|
|
1248
|
+
block.vegetation? # => false
|
|
1249
|
+
```
|
|
1250
|
+
|
|
1251
|
+
**Access texture through block_type:**
|
|
1252
|
+
|
|
1253
|
+
```ruby
|
|
1254
|
+
block.texture_path # => "/path/to/Rock_Quartzite.png"
|
|
1255
|
+
block.texture_exists? # => true
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
**Finding surface blocks:**
|
|
1259
|
+
|
|
1260
|
+
```ruby
|
|
1261
|
+
surface = chunk.surface_at(8, 8)
|
|
1262
|
+
surface.id # => "Rock_Bedrock"
|
|
1263
|
+
surface.y # => 126
|
|
1264
|
+
surface.world_position # => [-8, 126, -280]
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
**Performance note:** For bulk operations, use `block_type_at(x, y, z)` which returns just the string ID without creating Block instances:
|
|
1268
|
+
|
|
1269
|
+
```ruby
|
|
1270
|
+
type_id = chunk.block_type_at(8, 50, 8) # => "Rock_Quartzite"
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
### Player Skins
|
|
1274
|
+
|
|
1275
|
+
Access cached player skin/cosmetic data:
|
|
1276
|
+
|
|
1277
|
+
**List all cached skins:**
|
|
1278
|
+
|
|
1279
|
+
```ruby
|
|
1280
|
+
Hytale.client.player_skins
|
|
1281
|
+
# => [PlayerSkin: 79816d74-..., ...]
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
**Find a specific skin:**
|
|
1285
|
+
|
|
1286
|
+
```ruby
|
|
1287
|
+
skin = Hytale.client.player_skin("00000000-0000-0000-0000-000000000000")
|
|
1288
|
+
skin.uuid # => "00000000-0000-0000-0000-000000000000"
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**Appearance:**
|
|
1292
|
+
|
|
1293
|
+
```ruby
|
|
1294
|
+
skin.body_characteristic # => "Muscular.06"
|
|
1295
|
+
skin.face # => "Face_Stubble"
|
|
1296
|
+
skin.eyes # => "Medium_Eyes.BrownDark"
|
|
1297
|
+
skin.haircut # => "VikinManBun.BrownDark"
|
|
1298
|
+
skin.facial_hair # => "Groomed_Large.BrownDark"
|
|
1299
|
+
skin.eyebrows # => "Square.BrownDark"
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
**Clothing:**
|
|
1303
|
+
|
|
1304
|
+
```ruby
|
|
1305
|
+
skin.pants # => "BulkySuede.Brown"
|
|
1306
|
+
skin.overpants # => "LongSocks_Bow.Pink"
|
|
1307
|
+
skin.undertop # => "VikingShirt.Black"
|
|
1308
|
+
skin.overtop # => nil
|
|
1309
|
+
skin.shoes # => "HeavyLeather.Black"
|
|
1310
|
+
skin.gloves # => "FlowerBracer.Gold_Red"
|
|
1311
|
+
skin.cape # => nil
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
**Accessories:**
|
|
1315
|
+
|
|
1316
|
+
```ruby
|
|
1317
|
+
skin.head_accessory # => nil
|
|
1318
|
+
skin.face_accessory # => nil
|
|
1319
|
+
skin.ear_accessory # => "SimpleEarring.Gold_Red.Right"
|
|
1320
|
+
```
|
|
1321
|
+
|
|
1322
|
+
**Utility methods:**
|
|
1323
|
+
|
|
1324
|
+
```ruby
|
|
1325
|
+
skin.equipped_items # => {"bodyCharacteristic" => "Muscular.06", ...}
|
|
1326
|
+
skin.empty_slots # => ["overtop", "headAccessory", "cape", ...]
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
**Avatar preview:**
|
|
1330
|
+
|
|
1331
|
+
```ruby
|
|
1332
|
+
skin.avatar_preview_path # => "/path/to/CachedAvatarPreviews/uuid.png"
|
|
1333
|
+
skin.avatar_preview_data # => PNG binary data
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
**Texture paths:**
|
|
1337
|
+
|
|
1338
|
+
```ruby
|
|
1339
|
+
skin.haircut_texture_path # => "/path/to/assets/Common/Characters/Haircuts/Viking_Topknot_Greyscale.png"
|
|
1340
|
+
skin.pants_texture_path # => "/path/to/assets/Common/Cosmetics/Pants/Pants_Brown.png"
|
|
1341
|
+
skin.shoes_texture_path # => "/path/to/assets/Common/Cosmetics/Shoes/HeavyLeather_Black.png"
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
**Get all texture paths at once:**
|
|
1345
|
+
|
|
1346
|
+
```ruby
|
|
1347
|
+
skin.texture_paths
|
|
1348
|
+
# => {haircut: "/path/to/...", pants: "/path/to/...", ...}
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
### Cosmetics
|
|
1352
|
+
|
|
1353
|
+
Access the cosmetic item catalog:
|
|
1354
|
+
|
|
1355
|
+
**Look up cosmetic items:**
|
|
1356
|
+
|
|
1357
|
+
```ruby
|
|
1358
|
+
Hytale::Client::Cosmetics.find(:haircuts, "VikinManBun")
|
|
1359
|
+
# => {"Id" => "VikinManBun", "Model" => "...", "GreyscaleTexture" => "..."}
|
|
1360
|
+
|
|
1361
|
+
Hytale::Client::Cosmetics.find(:pants, "BulkySuede")
|
|
1362
|
+
# => {"Id" => "BulkySuede", "Model" => "...", "Textures" => {...}}
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
**Get texture and model paths:**
|
|
1366
|
+
|
|
1367
|
+
```ruby
|
|
1368
|
+
Hytale::Client::Cosmetics.texture_path(:haircuts, "VikinManBun.BrownDark")
|
|
1369
|
+
# => "/path/to/assets/Common/Characters/Haircuts/Viking_Topknot_Greyscale.png"
|
|
1370
|
+
|
|
1371
|
+
Hytale::Client::Cosmetics.model_path(:haircuts, "VikinManBun")
|
|
1372
|
+
# => "/path/to/assets/Common/Characters/Haircuts/Viking_TopKnot.blockymodel"
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
**Available cosmetic types:**
|
|
1376
|
+
|
|
1377
|
+
| Type | Description |
|
|
1378
|
+
|------|-------------|
|
|
1379
|
+
| `:haircuts` | Hair styles |
|
|
1380
|
+
| `:facial_hair` | Beards, mustaches |
|
|
1381
|
+
| `:eyebrows` | Eyebrow styles |
|
|
1382
|
+
| `:eyes` | Eye styles |
|
|
1383
|
+
| `:faces` | Face textures |
|
|
1384
|
+
| `:pants` | Pants/bottoms |
|
|
1385
|
+
| `:overpants` | Socks, leg accessories |
|
|
1386
|
+
| `:undertops` | Shirts, undershirts |
|
|
1387
|
+
| `:overtops` | Jackets, vests |
|
|
1388
|
+
| `:shoes` | Footwear |
|
|
1389
|
+
| `:gloves` | Gloves, bracers |
|
|
1390
|
+
| `:capes` | Capes |
|
|
1391
|
+
| `:head_accessories` | Hats, helmets |
|
|
1392
|
+
| `:face_accessories` | Glasses, masks |
|
|
1393
|
+
| `:ear_accessories` | Earrings |
|
|
1394
|
+
|
|
1395
|
+
### Assets
|
|
1396
|
+
|
|
1397
|
+
The gem automatically extracts and caches all game assets from `Assets.zip` on first use.
|
|
1398
|
+
|
|
1399
|
+
**Asset cache location:**
|
|
1400
|
+
|
|
1401
|
+
```ruby
|
|
1402
|
+
Hytale::Client::Assets.cache_path # => "/path/to/gem/assets"
|
|
1403
|
+
Hytale::Client::Assets.count # => 57708
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
**List asset directories:**
|
|
1407
|
+
|
|
1408
|
+
```ruby
|
|
1409
|
+
Hytale::Client::Assets.directories
|
|
1410
|
+
# => ["Common/BlockTextures", "Common/Blocks", "Common/Items", "Server/Prefabs", ...]
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
**Access any asset:**
|
|
1414
|
+
|
|
1415
|
+
```ruby
|
|
1416
|
+
Hytale::Client::Assets.cached?("Common/Icons/Item_Sword_Copper.png") # => true
|
|
1417
|
+
Hytale::Client::Assets.read("Common/Icons/Item_Sword_Copper.png") # => PNG binary data
|
|
1418
|
+
Hytale::Client::Assets.cached_path("Common/Icons/Item_Sword_Copper.png")
|
|
1419
|
+
# => "/path/to/gem/assets/Common/Icons/Item_Sword_Copper.png"
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**List files in a directory:**
|
|
1423
|
+
|
|
1424
|
+
```ruby
|
|
1425
|
+
Hytale::Client::Assets.list("Common/Icons")
|
|
1426
|
+
# => ["Common/Icons/Item_Sword_Copper.png", ...]
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
**Clear the cache:**
|
|
1430
|
+
|
|
1431
|
+
```ruby
|
|
1432
|
+
Hytale::Client::Assets.clear!
|
|
1433
|
+
```
|
|
1434
|
+
|
|
1435
|
+
## Development
|
|
1436
|
+
|
|
1437
|
+
```bash
|
|
1438
|
+
git clone https://github.com/marcoroth/hytale-ruby
|
|
1439
|
+
cd hytale
|
|
1440
|
+
bundle install
|
|
1441
|
+
bundle exec rake test
|
|
1442
|
+
```
|
|
32
1443
|
|
|
33
1444
|
## Contributing
|
|
34
1445
|
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/hytale.
|
|
1446
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/hytale-ruby.
|
|
1447
|
+
|
|
1448
|
+
## License
|
|
1449
|
+
|
|
1450
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
36
1451
|
|
|
37
|
-
##
|
|
1452
|
+
## Disclaimer
|
|
38
1453
|
|
|
39
|
-
|
|
1454
|
+
This gem is not affiliated with or endorsed by Hypixel Studios.
|