Home Page
Amazing Core
Amazing Core is an open-source server emulator for Amazing World, an MMO originally developed by Ganz and shut down in 2018. This project provides a modular, configurable framework with tools for server management, asset handling, and game services, accessible via a web-based dashboard.
⚠️ Still in development - not yet in a playable state.
- No multiplayer, NPCs, or quests yet;
- Only the intro level and the empty Spring Bay map are accessible;
- Do not use your real username or password;
- Use any dummy username and password to log in;
But you can check out the work-in-progress prototype and join our community!
Download the game
You can install the latest published version with the following Steam link: Install
The game has its page on SteamDB, where you can also see additional information.
It is also possible to find an Android version on the internet.
Connect to the demo server
After installation, navigate to the game folder and open the ServerConfig.xml file in a text editor.
Modify the server address value as shown below:
ServerIP = 'springbay.amazingcore.org'
Now you can start the game.
- To play the intro level, click the
I'm new!button in the main menu; - To explore the Spring Bay, click the
Log inbutton and enter any username and password;
Host your own server
With your own server, you can access the admin dashboard to configure skins, maps, NPCs, and other features (work in progress)*.
Modify the server address value as shown below:
ServerIP = 'localhost'
Pre-compiled binaries
- Download the latest server release from the GitHub section.
- Extract the archive to a folder of your choice;
- Run the server binary;
Once started, it will download the blob.db file (~2 GB).*
When the download is finished, you can start the game.
- The API server will be available at http://localhost:3000
- Use
admin / adminto log in to the dashboard - The game server will listen on
localhost:8182
Configuration
You can customize server settings using the config.json file.
| Key | Description |
|---|---|
logger.level | Log verbosity: debug, debug+sql, info, warn, error |
logger.handler | Log format: pretty (colored, formatted), text, json |
servers.api | HTTP API bind address (e.g. localhost:3000) |
servers.game | TCP game server bind address (e.g. localhost:8182) |
settings.assetDeliveryURL | Base CDN URL sent to game clients |
settings.syncServerIP | Sync server IP sent in InitLocation responses |
settings.syncServerPort | Sync server port sent in InitLocation responses |
storage.blob.download | Auto-download blob.db on first start if missing |
storage.blob.url | URL to fetch blob.db from |
storage.databases.core | Path to core.db - main SQLite database |
storage.databases.blob | Path to blob.db - assets SQLite database |
storage.explorer | Enable the dashboard SQL explorer - only for testing! |
secure.auth.username | Dashboard admin username |
secure.auth.password | Dashboard admin password |
secure.session.key | Cookie session signing key |
secure.session.secure | Set Secure flag on session cookie (enable behind HTTPS) |
Running the game on macOS
The Steam version of the game is not compatible with modern macOS (Apple Silicon), but you can try following this guide.
License
This project is licensed under the GNU AGPL v3.
Amazing World™ is a registered trademark of Ganz. Amazing Core is an unofficial, fan-made project intended for personal and educational use only. It is not affiliated with or endorsed by Ganz or Amazing World™ in any way.
Cache Archive
Amazing World used Unity Streaming Assets, meaning things like images, audio, and asset bundles were stored on official servers and loaded by the game whenever they were needed. These files were also cached locally. Below is a list of known assets that have been found and shared by members of the community.
Loading cache list...
| No results. |
| Field | Value |
|---|---|
| No results. | |
| Type | Count |
|---|---|
| No results. | |
| Path | |
|---|---|
| No results. | |
Preview
Loading image...
Loading audio...
| File | Duration | Controls |
|---|---|---|
Loading file...
Loading archive...
Scene
Drag to rotate, right-click drag to pan, scroll to zoom. Some assets may not be centered.
Audio
| File | Duration | Controls |
|---|---|---|
| Loading... |
Images
| File | Image |
|---|---|
| Loading... |
Models
GSFOID Calculator
A simple calculator for checking the OID (a unique object identifier) used by the game.
- The
CDN IDis abase64representation ofGSF OID. - The
GSF OIDis anint64number that is sliced into specific groups of bits.
Build from source
Build using Go
To build the server from source, you will need Go 1.25 or newer:
make
# or
go build -o ./build/server ./cmd/server/main.go
To build and run with a single command:
make run
# or
go run ./cmd/server/main.go
You can choose between SQLite drivers by setting the CGO_ENABLED environment variable:
- Build with
CGO_ENABLED=0to usemodernc.org/sqlitedriver (default); - Build with
CGO_ENABLED=1to usegithub.com/mattn/go-sqlite3driver;
Folder structure
amazing-core/
├── cmd/
│ └── server/
│ └── main.go - server entry point
├── data/
│ ├── cache.json - asset metadata generated by cache-tool
│ └── sql/
│ ├── core_db/
│ │ ├── base.sql - core.db base migration
│ │ └── updates/ - incremental migrations
│ ├── blob_db/
│ │ └── base.sql - blob.db schema
│ └── queries/ - sql queries for admin dashboard explorer
├── internal/
│ ├── api/ - http server for admin dashboard and asset streaming
│ ├── game/ - game server and gsf messages handling
│ ├── network/ - tcp server protocol implementation
│ ├── services/ - business logic and database interaction
│ ├── config/ - configuration variables
│ └── lib/ - shared libraries (e.g. logging, helpers)
├── tools/ - development tools (e.g. asset importers)
└── web/ - embedded frontend for admin dashboard
Data and databases
The data/ directory contains .sql database migrations and a cache.json file with currently known asset files metadata.
SQLite Databases
For simplicity and portability, the project uses SQLite.
The data/sql/ directory is embedded into the server binary at compile time, and migrations are run automatically at startup.
core.db
Schema: data/sql/core_db/
The main SQLite database. Contains the classified list of available assets and random names used in the sign-up scene.
Managed by goose using migrations in data/sql/core_db/updates/. Shortcuts for creating, applying, and resetting migrations are available in the Makefile.
An initial squashed migration (data/sql/core_db/base.sql) includes both schema and seed data.
Use data/sql/core_db/squash.sh to squash update migrations into base.sql.
Files under data/sql/queries/ are named example SQL queries available to the admin dashboard`s SQL Explorer.
blob.db
Schema: data/sql/blob_db/
A separate SQLite database storing the raw binary content of game asset files.
Downloaded on startup if blob.download = true and the database is missing.
When a client requests /cdn/{cdnid}, the server fetches the blob column for that cdnid.
The database can be populated using the blob-tool or the admin dashboard.
Cache Metadata
cache.json
A summary of known cache files, including name (CDN ID), size, type, hash, GSF OID and basic bundle info (version, object counts, scene roots).
Generated with the cache-tool Python script:
python tools/cache-tool/cache.py /path/to/cache/folder \
--summary-file data/cache.json
This file is already included in the
core.dbbase migration. For future updates (if new unique cache assets are discovered), add a new migration underupdates/with the additional data.
Networking
This section details the client-server communication protocol:
- Bit Protocol specification for message serialization.
- Go Implementation based on the
internal/network/package.
BitProtocol
Overview
The game client communicates with the server over a persistent TCP connection using a custom binary protocol. All data is serialized with the BitProtocol codec - a bit-level encoding scheme that compresses integers by encoding only the bytes that are actually needed.
The implementation lives in internal/network/
Wire Format
Every message follows this structure:
┌─────────────────────────────────────────────────────────┐
│ length prefix │ payload bytes │ 0x00 │
│ (varint) │ (BitStream encoding) │ (EOF) │
└─────────────────────────────────────────────────────────┘
Length prefix
The length prefix is a variable-length big-endian integer where the MSB of each byte signals whether more bytes follow:
Each group of 7 bits contributes to the value, MSB = continuation flag:
[1xxxxxxx] [1xxxxxxx] [1xxxxxxx] [0xxxxxxx]
more bytes more bytes more bytes last byte
Payload
The payload is a BitStream - a sequence of bits packed into bytes. The stream is read and written bit-by-bit; byte boundaries are only respected for raw string/byte-array data (aligned reads/writes).
After the last payload byte comes a null terminator 0x00.
BitStream Encoding
Objects (nullable)
Objects begin with a null flag bit:
[0] -> object is not null
[1] -> null / not present
Booleans
A single bit: 1 = true, 0 = false.
Compressed integers (int16, int32, int64)
Integers are compressed using a variable-width encoding that minimizes bit usage for small values.
The encoding starts with a compressed flag bit:
[1] -> compressed (fewer than max bytes needed)
followed by a count of how many bytes are needed
[0] -> 0 bytes -> value fits in 4 bits
[10] -> 1 byte -> value is 1 byte (8 bits)
[110] -> 2 bytes -> value is 2 bytes (16 bits)
...
[0] -> full width (all bytes written)
Floating-point numbers (float32, float64)
Written as raw not compressed IEEE 754 bytes.
Strings
Strings are encoded as:
- A compressed
int32for the UTF-8 byte length. - The raw UTF-8 bytes, byte-aligned (any remaining bits in the current byte are skipped before writing).
Byte arrays
Same as strings: compressed length prefix followed by byte-aligned data.
Dates
Dates are encoded relative to a fixed epoch (0001-03-01).
A null flag bit precedes the value:
[1] -> zero time (null)
[0] -> seconds since epoch, written as a raw 8-byte integer
Message Structure
Every message has a header followed by an optional body. The outer wrapper adds a null flag for the entire message object.
[0] -> message is not null
[header] -> GSF header fields
[0] -> body is not null
[body fields]
GSF Header
| Field | Type | Condition | Description |
|---|---|---|---|
flags | int32 | always | Bit flags |
svcClass | int32 | always | Message service class |
msgType | int32 | always | Message type for the given svcClass |
requestId | int32 | if IsService() | Correlates request/response pairs |
logCorrelator | string | if IsRequest() | Client-side correlation string |
resultCode | int32 | if IsResponse() | Result code (0 = success) |
appCode | int32 | if IsResponse() | Application-level error code |
appString | string | if appCode != 0 | Error description string |
appCodes | array | if appCode == 17 | Extended error code list |
Flag semantics:
| Bit | Meaning |
|---|---|
flags & 2 == 0 | IsService - a request/response pair |
flags & 1 != 0 | IsResponse (only when IsService) |
flags & 2 != 0 | IsNotify - fire-and-forget notification |
flags & 16 != 0 | IsDiscardable |
Annotated Example
The very first message the client sends after connecting is a GetClientVersionInfo request. It is 22 bytes total:
hex: 15 20 c2 5c 04 6d 0c 0c 18 41 6d 61 7a 69 6e 67 57 6f 72 6c 64 00
Breakdown:
| Bytes | Value | Meaning |
|---|---|---|
15 | 21 | Length prefix: payload is 21 bytes |
20 c2 5c 04 6d 0c 0c 18 | bit stream | Header + body |
41 6d 61 7a 69 6e 67 57 6f 72 6c 64 | AmazingWorld | String content (byte-aligned) |
00 | 0 | Null terminator |
Bit-by-bit payload decoding:
Bit 8: [0] -> message is not null
Bit 9: [0] -> header is not null
Bit 10: [1][0] -> flags compressed, 0 bytes -> 4-bit value
Bit 14: [0000] -> flags = 0
Bit 16: [1][1][0] -> svcClass compressed, 1 byte follows
Bit 19: [00010010] -> svcClass = 18 (USER_SERVER)
Bit 27: [1][1][1][0] -> msgType compressed, 2 bytes follow
Bit 31: [0000001000110110] -> msgType = 566 (GET_CLIENT_VERSION_INFO)
Bit 47: [1][0] -> requestId compressed, 0 bytes -> 4-bit value
Bit 51: [0001] -> requestId = 1
Bit 53: [1][0] -> logCorrelator length compressed, 0 bytes -> 4-bit value
Bit 57: [0000] -> length = 0 (empty string)
Bit 59: [0] -> body is not null
Bit 60: [1][1][0] -> clientName length compressed, 1 byte follows
Bit 63: [00001100] -> length = 12 (bytes in "AmazingWorld")
Bit 71: [0] align -> skip to byte boundary
Bits 72–167: -> "AmazingWorld" (12 bytes, 96 bits)
Go Implementation
Reading a message
// 1. Read framing length from buffered reader
length, err := codec.ReadLength(stream)
// 2. Read 'length' bytes into data buffer
data := make([]byte, length)
io.ReadFull(stream, data)
// 3. Create a BitReader over the raw bytes
reader := bitprotocol.NewBitReader(data)
// 4. Parse the header
header, err := gsf.ReadHeader(reader)
// 5. Look up the handler
handler, ok := router.Lookup(header.SvcClass, header.MsgType)
// 6. Build request and response objects
conn := &gsf.Connection{remoteIP: remoteAddr}
req := gsf.NewRequest(ctx, header, reader, conn)
res := gsf.NewResponse(header, writer)
// 7. Call the handler
handler(res, req)
Implementing a handler
func GetClientVersionInfo(w gsf.ResponseWriter, r *gsf.Request) error {
// Decode the request body
req := &messages.GetClientVersionInfoRequest{}
if err := r.Read(req); err != nil {
return err
}
// Build and send the response
res := &messages.GetClientVersionInfoResponse{
ClientVersionInfo: "133852.true",
}
return w.Write(res)
}
Defining a message type
Request and response structs implement gsf.Deserializable and gsf.Serializable:
type GetClientVersionInfoRequest struct {
ClientName string
}
func (req *GetClientVersionInfoRequest) Deserialize(reader gsf.ProtocolReader) {
req.ClientName = reader.ReadString()
}
type GetClientVersionInfoResponse struct {
ClientVersionInfo string
}
func (res *GetClientVersionInfoResponse) Serialize(writer gsf.ProtocolWriter) {
writer.WriteString(res.ClientVersionInfo)
}
Collection helpers
The gsf package provides generic helpers for encoding and decoding slices, maps, and nullable values:
// Slices: int32 length prefix (−1 = nil), then elements
gsf.ReadSlice(reader, func() MyType { ... })
gsf.WriteSlice(writer, slice, func(v MyType) { ... })
// Maps: int32 length prefix (−1 = nil), then string key + value pairs
gsf.ReadMap(reader, func() MyType { ... })
gsf.WriteMap(writer, dict, func(v MyType) { ... })
// Nullable objects: bool null flag + optional value
gsf.ReadNullable(reader, func() MyType { ... })
gsf.WriteNullable(writer, value, func(v MyType) { ... })
GSF Classes
This section documents game requests and responses grouped by flow.
Each request is a GSF message sent by the client to the server.
Assembly-CSharp
The game is built using Unity engine. The latest version of the game was compiled with 5.3.6f1.
Most of the game’s core logic is contained in the Assembly-CSharp.dll file.
This DLL may be decompiled, modified, and recompiled using common .NET C# tools such as:
ILSpy: primary decompiler, successfully decompiles nearly all code without issues;dnSpy: used for edge cases where ILSpy fails (in this project, only a single file required it);
Startup Messages
On game start, before the main menu becomes interactive, AkamaiServerCheck opens a temporary session to perform a version check, then closes it.
| # | Message | Invoked at |
|---|---|---|
| 1 | GetClientVersionInfo | AkamaiServerCheck.cs:70 |
GetClientVersionInfo
Description
Sent on game start, before the main menu is interactive.
A temporary session is created for this check.
The client needs to know whether it is running an outdated version of the game.
On receiving the response, it compares the server version number against GameSettings.clientLocalVersion.
Request
| Field | Type | Description |
|---|---|---|
ClientName | string | Hardcoded to "AmazingWorld" |
Response
| Field | Type | Description |
|---|---|---|
ClientVersionInfo | string | Version string in "<version>.<forceUpdate>" format, e.g. "133852.true". The forceUpdate part is either true (blocking) or false (optional). |
System Messages
Request and response messages used in multiple flows.
GetSiteFrame
Description
- Sent when the intro scene begins loading (on registration).
- Sent after tiers are loaded, if
SessionIDis valid (on login).
Fetches the site frame configuration for the current session.
The response also carries AssetDeliveryURL, which the client stores in GameSettings.assetDeliveryURL and uses for asset downloads.
Request
| Field | Type | Description |
|---|---|---|
TypeValue | int32 | Hardcoded to 1 |
LanglocalePairID | OID | “Class”: 4, “Type”: 19 “Server”: 0, “Number”: 9023265 |
TierID | OID | “Class”: 0, “Type”: 0, “Server”: 0, “Number”: 0 |
BirthDate | time.Time | #N/A |
RegistrationDate | time.Time | #N/A |
PreviewDate | time.Time | #N/A |
IsPreviewEnabled | bool | false |
Response
| Field | Type | Description |
|---|---|---|
SiteFrame | SiteFrame | SiteFrame object |
AssetDeliveryURL | string | Base URL used for asset downloads |
Registration Messages
When the player clicks “I’m new!” in the main menu, the intro sequence begins.
Player chooses a name, a family name, and account credentials.
| # | Message | Invoked at |
|---|---|---|
| 1 | GetPublicItemCategories | USPopulateIntroInventoryGrid.cs:46 |
| 2 | GetSiteFrame | IntroManager.cs:156 |
| 3 | GetRandomNames | IntroServiceCallManager.cs:74,88 |
| 4 | ValidateName | USValidateName.cs:36, USRegisterPlayer.cs:151 |
| 5 | SelectPlayerName | USWorldName.cs:349 |
| 6 | CheckUsername | IntroServiceCallManager.cs:111, RegistrationManager.cs:52 |
| 7 | RegisterPlayer | IntroServiceCallManager.cs:135, USRegisterPlayer.cs:125 |
| 8 | RegisterAvatarForRegistration | IntroServiceCallManager.cs:322 |
GetPublicItemCategories
Description
This is the public item-category lookup used by the registration flow.
Its purpose is to give the intro scene enough category data to classify and render preview items that are shown before the player has fully registered.
The returned categories are added into InventoryManager and then used when the public items are converted into temporary GSFPlayerItems.
This message is for public preview content, while GetCMSItemCategories is for the actual logged-in game state.
Request
| Field | Type | Description |
|---|---|---|
LanglocalePairID | OID | “Class”: 4, “Type”: 19 “Server”: 0, “Number”: 9023265 |
TierID | OID | “Class”: 0, “Type”: 0, “Server”: 0, “Number”: 0 |
BirthDate | time.Time | #N/A |
RegistrationDate | time.Time | #N/A |
PreviewDate | time.Time | #N/A |
IsPreviewEnabled | bool | false |
Response
| Field | Type | Description |
|---|---|---|
ItemCategories | []ItemCategory | List of item categories |
GetRandomNames
Description
Sent in two situations during the intro sequence:
- When the player requests a random Zing name;
- When building the family name picker;
Returns a list of randomly selected names of the requested type.
Request
| Field | Type | Description |
|---|---|---|
Amount | int32 | Number of names to return |
NamePartType | string | "second_name" for Zing names, "Family_1", "Family_2", "Family_3" for family name parts |
Response
| Field | Type | Description |
|---|---|---|
Names | []string | List of randomly selected names |
ValidateName
Description
Sent when the player confirms their Zing name during the sign up.
Checks whether the chosen player name contains any filtered or prohibited words.
Request
| Field | Type | Description |
|---|---|---|
Name | string | The player name to validate |
Response
| Field | Type | Description |
|---|---|---|
FilterName | string | The client does not really care about the value. A non-empty value means the name was rejected. |
SelectPlayerName
Description
Sent when the player confirms their family name by pressing the Next button.
Reserves the chosen family name on the server so no other player can claim it during the same session(?).
Request
| Field | Type | Description |
|---|---|---|
Name | string | The family name to reserve |
Response
No fields.
Success is implied by a non-error response.
On AppCode 71 (duplicate) the client shows an error dialog.
CheckUsername
Description
Sent when the player submits the registration form.
Verifies that the chosen username is not already taken.
Request
| Field | Type | Description |
|---|---|---|
Username | string | The username to check |
Password | string | The account password |
Response
No fields.
Success is implied by a non-error response.
On AppCode 301 the server signals the username is already in use.
RegisterPlayer
Description
Sent when the player submits the registration form.
Creates the player account on the server.
On success, the returned PlayerID is immediately used by the client to call RegisterAvatarForRegistration,
which associates the chosen avatar and starter items with the new account.
Request
| Field | Type | Description |
|---|---|---|
Token | string | |
Password | string | Chosen password |
ParentEmailAddress | string | Chosen email |
BirthDate | time.Time | |
Gender | string | Player gender (always "U" for unspecified) |
LocationID | OID | “Class”: 0, “Type”: 0, “Server”: 0, “Number”: 0 |
Username | string | Chosen username |
Worldname | string | Chosen family name |
ChatAllowed | bool | Whether the player has enabled chat |
CNL | string | Channel: steam |
ReferredByWorldname | string | |
LoginType | int32 |
Response
| Field | Type | Description |
|---|---|---|
PlayerID | OID | The newly created player’s OID |
RegisterAvatarForRegistration
Description
Sent after a successful RegisterPlayer response.
Associates the chosen avatar and starter inventory items with the newly created player account.
Request
| Field | Type | Description |
|---|---|---|
PlayerID | OID | The OID returned by RegisterPlayer |
SecretCode | string | |
Name | string | Avatar name |
Bio | string | |
AvatarID | OID | OID of the selected avatar |
GivenInventoryIDs | []OID | List of starter item OIDs selected during intro |
GivenItemSlotIDs | []OID | List of inventory slot OIDs for the starter items |
Response
| Field | Type | Description |
|---|---|---|
PlayerAvatar | PlayerAvatar | The created avatar object |
InvalidCodeCount | int32 | Number of invalid secret code attempts |
InvalidCodeThreshold | int32 | Maximum allowed invalid code attempts |
Login Messages
When the player submits their credentials on the login screen, the client opens a session and begins the login sequence.
After the initial login response the client runs two load queues:
-
ClientServices: starts in
ClientManager.StartLogin(), initialises background services (including connection to the SYNC server). -
ZoneTransitionQueue: starts in
ClientManager.LoadZone()after theSiteFrameassets are preloaded, runs the full zone-entry sequence.
After the SiteFrame load, AnimationLoaded() is then called inline, which starts preloading assets.
| # | Message | Invoked at |
|---|---|---|
| 1 | Login | ClientManager.cs:567 |
| 2 | GetTiers | ClientManager.cs:652 |
| 3 | GetSiteFrame | ClientManager.cs:647 |
| 4 | GetCMSItemCategories | InventoryManager.cs:846 |
| 5 | GetOutfitItems | LoadCurrentOutfitCommand.cs:5 |
| 6 | GetAvatars | LoadAvatarsCommand.cs:13 |
| 7 | GetOutfits | AvatarAssetsLoader.cs:138 |
| 8 | GetZones | LoadZonesCommand.cs:18 |
| 9 | InitLocation | LoadHomeZoneCommand.cs:23 |
| 10 | SyncLogin | SyncManager.cs:331 |
| 11 | GetMazeItems | LoadMazeItemsCommand.cs:28 |
| 12 | GetChatChannelTypes | LoadChatChannelTypesCommand.cs:7 |
| 13 | GetAnnouncements | LoadGlobalAnnouncementsCommand.cs:11 |
| 14 | EnterBuilding | LoadEnterBuildingMazeCommand.cs:49 |
| 15 | GetOnlineStatuses | InitFriendMangerNotificationManagerCommand.cs:9 |
| 16 | GetPlayerNPCs | LoadNPCsCommand.cs:29 |
Login
Description
Sent when the player submits the login form.
Request
| Field | Type | Description |
|---|---|---|
LoginID | string | Player username |
Password | string | Player password |
SitePIN | int32 | Hardcoded to 1234 |
LanguageLocalePairID | OID | “Class”: 4, “Type”: 19, “Server”: 0, “Number”: 9023265 |
UserQueueingToken | string | Hardcoded to "Token" |
ClientEnvInfo | ClientEnvironmentData | OS, resolution, Unity version, etc. |
Token | string | |
LoginType | int32 | |
CNL | string | Channel: steam |
Response
| Field | Type | Description |
|---|---|---|
SiteInfo | SiteInfo | |
Status | SessionStatus | |
SessionID | OID | |
ConversationID | int64 | |
AssetDeliveryURL | string | Base URL used for asset downloads |
Player | Player | |
MaxOutfit | int16 | |
PlayerStats | []PlayerStats | |
PlayerInfoTO | PlayerInfoTO | |
CurrentServerTime | time.Time | |
SystemLockoutTime | time.Time | |
SystemShutdownTime | time.Time | |
ClientInactivityTimeout | int32 | |
CNL | string |
GetTiers
Description
Sent immediately after a successful login response.
Fetches all subscription tiers. The client matches the player’s tierID from PlayerInfoTO against the returned list to determine whether the account is paid or free-play.
On success, GetSiteFrame() is called if SessionID is valid.
Request
No fields.
Response
| Field | Type | Description |
|---|---|---|
Tiers | []Tier | List of tiers |
GetCMSItemCategories
Description
This is the main item-category lookup for the logged-in game.
The response is cached into InventoryManager.itemCategories, and many systems rely on that cache to map category OIDs to semantic item types such as Clothing, Decoration, Yard, MazePiece, and so on.
Request
No fields.
Response
| Field | Type | Description |
|---|---|---|
ItemCategories | []ItemCategory | List of item categories |
GetOutfitItems
Description
Sent as the first step of the ZoneTransitionQueue.
Fetches the items currently equipped(?) by the player’s active avatar.
Request
| Field | Type | Description |
|---|---|---|
PlayerAvatarOutfitID | OID | Requested avatar outfit OID |
PlayerID | OID | Requested player OID |
Response
| Field | Type | Description |
|---|---|---|
OutfitItems | []PlayerItem | List of player items |
GetAvatars
Description
Sent during the ZoneTransitionQueue, after outfit items are loaded.
Fetches the list of player avatars. The returned list is stored in AvatarManager.Instance.GSFPlayerAvatars.
LoadAvatarsCommand.Step2() calls AvatarManager.LoadAvatarAssets() for the active player avatar, which in turn calls GetOutfits.
The active player avatar must be present in the returned array. (?)
Each PlayerAvatar.Avatar.AssetMap must contain a "Prefab_Unity3D" entry with at least one asset whose ResName is not "PF__Avatar.unity3d". (?)
Request
| Field | Type | Description |
|---|---|---|
Start | int32 | Start index, 0 |
Max | int32 | Max results, -1 for all |
FilterIDs | []OID | Empty filter list |
Response
| Field | Type | Description |
|---|---|---|
Avatars | []PlayerAvatar | List of player avatars |
GetOutfits
Description
Sent during the ZoneTransitionQueue after avatars are loaded, once per avatar whose assets are being loaded.
Fetches the saved outfits for a given player avatar. The results are stored as PresetOutfits on the AvatarAssets object.
Request
Response
| Field | Type | Description |
|---|---|---|
PlayerAvatarOutfits | []PlayerAvatarOutfit | List of avatar outfits |
GetZones
Description
Sent during the ZoneTransitionQueue.
Fetches the list of available zones. The list is stored in ZoneManager.Instance.zones.
LoadNPCsCommand later calls SpawnPoints.Instance.ParseZone(NPCManager.HardCodedZoneId), so the NPC zone must be present in this list.
The array must include the NPC zone with OID { Class: 4, Type: 16, Server: 0, Number: 2937912 } (NPCManager.HardCodedZoneId). (?)
Request
No fields.
Response
| Field | Type | Description |
|---|---|---|
Zones | []Zone | List of zones |
InitLocation
Description
Sent during the ZoneTransitionQueue queue, after zones are loaded.
Returns the player’s home location data and the address of the SYNC server.
The Home.PlayerMaze.HomeTheme.AssetMap["Scene_Unity3D"] asset drives scene loading via LoadMazeCommand -> AssetDownloadManager.LoadMainScene().
Home.PlayerMaze.HomeTheme.AssetMap must contain a "Scene_Unity3D" entry used by LoadMazeCommand to load the main scene asset,
e.g. Springtime003.unity3d (main map) or HomeLotSmall.unity3d (home lot). (?)
ZoneManager.InitLocationHomeResponseHandler() stores syncServerIP, syncServerToken, and syncServerPort in GameSettings.
These are later used by SyncManager to open its session and send SyncLogin.
Request
| Field | Type | Description |
|---|---|---|
LocID | OID |
Response
| Field | Type | Description |
|---|---|---|
ZoneInstance | ZoneInstance | |
Village | Village | |
Home | PlayerHome | |
SyncServerToken | string | Token used to authenticate with the SYNC server |
SyncServerIP | string | IP address of the SYNC server |
SyncServerPort | int32 | Port of the SYNC server |
SyncLogin
Description
Sent when the SYNC server opens TCP session.
The SYNC session is started by SyncManager.Instance.Start(), which is called from LoadServicesCommand.cs:21.
The sync server address and token come from the InitLocation response stored in GameSettings.
Authenticates the player on the SYNC server. The SYNC server handles real-time positional and social updates.
Request
| Field | Type | Description |
|---|---|---|
UID | OID | |
Token | string | Sync server token from the InitLocation response |
MaxVisSize | int32 |
Response
No fields.
GetMazeItems
Description
Sent during the ZoneTransitionQueue, after the home location is initialised.
Fetches the items placed in the player’s current maze.
Request
Response
| Field | Type | Description |
|---|---|---|
MazeItems | []PlayerItem | List of player items |
GetChatChannelTypes
Description
Sent during the ZoneTransitionQueue.
Fetches the available chat channel type definitions.
Request
No fields.
Response
| Field | Type | Description |
|---|---|---|
ChatChannelTypes | []ChatChannelType | List of chat channel types |
GetAnnouncements
Description
Sent during the ZoneTransitionQueue.
Fetches login announcements to be displayed to the player on entry.
Request
| Field | Type | Description |
|---|---|---|
UnMarked | bool |
Response
| Field | Type | Description |
|---|---|---|
Announcements | []Announcement | List of announcements |
EnterBuilding
Description
Sent during the ZoneTransitionQueue, after announcements are loaded.
Notifies the server that the player is entering a building.
During initial login with no active building context, this is a pass-through step. (?)
Request
| Field | Type | Description |
|---|---|---|
LocID | OID | OID of the zone (?) |
BuildingID | OID | OID of the building |
Pos | Position | Player position |
Orientation | QTH | Player orientation (quaternion) |
Response
| Field | Type | Description |
|---|---|---|
BuildingID | OID | OID of the building entered |
GetOnlineStatuses
Description
Sent during the ZoneTransitionQueue.
Fetches the online statuses of the player’s friends. The result is stored in FriendManager.Instance.statusList.
Only sent once per session (InitFriendMangerNotificationManagerCommand.hasDone guards against repetition).
Request
No fields.
Response
| Field | Type | Description |
|---|---|---|
OnlineStatuses | []OnlineStatus | List of online statuses |
GetPlayerNPCs
Description
Sent during the ZoneTransitionQueue, as the last major data fetch.
Fetches the NPCs for the player’s home zone.
SpawnPoints.Instance.ParseZone(NPCManager.HardCodedZoneId) is called beforehand to set up spawn points - this requires the NPC zone to have been returned by GetZones.
Request
Response
| Field | Type | Description |
|---|---|---|
NPCs | []NPC | List of NPCs |
Object Types
This section documents game GSF object types.
Announcement
Asset
Description
Represents a single downloadable game file.
Fields
| Field | Type | Description |
|---|---|---|
OID | OID | OID identifier |
AssetTypeName | string | Asset classification (e.g. "Prefab_Unity3D") |
CDNID | string | CDN identifier used to construct the download URL: AssetDeliveryURL + CDNID |
ResName | string | Resource name |
GroupName | string | Group classification (e.g. "Main_Scene", "3D Components", "Locked") |
FileSize | int64 | File size in bytes |
AssetContainer
Description
A container that groups assets by type and bundles optional asset packages.
Fields
| Field | Type | Description |
|---|---|---|
OID | OID | Container identifier |
AssetMap | map[string][]Asset | Dictionary keyed by AssetTypeName containing a list of assets |
AssetPackages | []AssetPackage | List of asset packages for conditional/maze-specific asset groups |
AssetPackage
Description
An AssetContainer with an additional tag used to conditionally load assets depending on which maze the player is entering.
Fields
| Field | Type | Description |
|---|---|---|
OID | OID | Inherited from AssetContainer |
AssetMap | map[string][]Asset | Inherited from AssetContainer |
AssetPackages | []AssetPackage | Inherited from AssetContainer |
PTag | string | Tag matched against the current maze name to decide whether this package is loaded |
CreateDate | time.Time |
PTag matching
During LoadMazeCommand:238, the client iterates all packages from the home theme and applies this rule:
PTag == maze.name-> package is loaded (maze-specific assets)PTag == ""(empty) -> package is always loaded (shared assets)PTagis anything else -> package is skipped
This allows the server to ship assets for multiple different mazes in a single response, while the client only downloads what is relevant to the current location. (?)
ChatChannelType
ClientEnvironmentData
ItemCategory
OID
Description
A unique object GSFOID identifier usually stored as a big int64 number, that can be sliced into specific groups of bits to get the Class, Type, Server and Object number.
[Class][Type][Server][Number...]
8b 8b 8b 40b
Fields
| Field | Type | Description |
|---|---|---|
Class | byte | For cache files is always 0 |
Type | byte | For cache files is always 0 |
Server | byte | Server version? Goes from 1 to 8 (higher = newer asset) |
Number | int64 | Object number |
Online Status
Player
PlayerAvatar
PlayerAvatarOutfit
PlayerHome
PlayerInfoTO
PlayerItem
PlayerStats
SessionStatus
SiteFrame
SiteInfo
Tier
NPC
Village
Zone
ZoneInstance
QA Tools Menu
The game contains a hidden QA tools menu that can be enabled through ServerConfig.xml.
Enabling it
Add a Version attribute to the config:
Version = '1.8'
After launching the game with this flag enabled, press F3 to open the QA tools menu.
F2 also should open a related QA layout, but it probably requires an additional UI file.
How It Works
The Version attribute is obfuscated, and is not treated as a normal version number.
The client decodes it as an encoded options flag in GameSettings.cs:748.
- Parse the value as a float.
- Multiply it by 10.
- Round it to an integer.
- If the result is divisible by 9, accept it as a valid options code.
- Divide it by 10 and interpret the result as a bitmask.
Bitmask Values
The decoded bitmask controls three flags:
1 -> debuggingEnabled
2 -> skipIntro
4 -> showVersion
In practice, valid values are:
1.8 -> debug enabled
2.7 -> skip intro
3.6 -> debug enabled + skip intro
4.5 -> show version
5.4 -> debug enabled + show version
6.3 -> skip intro + show version
7.2 -> debug enabled + skip intro + show version