GServer Behavior Reference
This document provides a quick reference for how GServer-v2 implements various protocol behaviors, based on analysis of the source code. This is meant to be used as a reference to avoid repeatedly searching through the GServer source.
String Encoding
Three Types of Strings in GServer
Regular String (read to end of packet)
CString level = packet.readString(""); // Empty delimiter = read to end
Used in: PLI_LEVELWARP, PLO_LEVELNAME, PLO_FILESENDFAILED
No length prefix, no terminator
Reads ALL remaining bytes in the packet
NOT null-terminated despite what some old docs say
Length-Prefixed String (GSTRING)
uint8_t length = packet.readGChar(); std::string text = packet.read(length);
Used in: Player properties, NPC properties, file packets
First byte is length (encoded as GCHAR)
Maximum length: 223 characters
Newline-Terminated String
std::string text = packet.readString("\n");
Used in: Level links, some packet handlers
Reads until newline (0x0A)
Newline is consumed but not included
Critical Packet Implementations
PLI_LEVELWARP (0)
// From PlayerClientPackets.cpp
HandlePacketResult PlayerClient::msgPLI_LEVELWARP(CString& pPacket)
{
time_t modTime = 0;
if (pPacket[0] - 32 == PLI_LEVELWARPMOD)
modTime = (time_t)pPacket.readGUInt5();
Position<int16_t> pos = {
static_cast<int16_t>(pPacket.readGChar() * 8),
static_cast<int16_t>(pPacket.readGChar() * 8)
};
CString newLevel = pPacket.readString(""); // Read to end!
// ...
}
Format: [GUCHAR: x][GUCHAR: y][STRING: level_name]
X/Y are in tile coordinates (multiply by 8 for pixels)
Level name reads to end of packet
Important: Server sends null-terminated strings (ends with \x00)
PLO_LEVELNAME (19)
// Server sends level name
sendPacket(CString() >> (char)PLO_LEVELNAME << level->levelName);
// For GMAP, sends map name instead
sendPacket(CString() >> (char)PLO_LEVELNAME << map->getMapName());
Format: [STRING: level_name]
Entire packet payload is the level name
No length prefix
Important: Server sends null-terminated strings (ends with \x00)
PLO_PLAYERPROPS (14) / PLO_OTHERPLPROPS (15)
Player properties use a special encoding:
Property ID (1 byte)
Property data (varies by property)
Special cases:
HEADGIF: If length < 100, it’s a preset head. Otherwise length-100 is string length
SWORDPOWER/SHIELDPOWER:
[length][power+30][image_string]X2/Y2: Encoded as
(abs(pixels) << 1) | (negative ? 1 : 0), sent as GShortGATTRIB1-30: Flag properties with no data
CString Operators
The GServer uses CString with special operators for packet construction:
// >> operator adds data with Graal encoding
CString packet;
packet >> (char)PLO_LEVELNAME; // Adds char + 32
packet << levelName; // Appends raw string
// Reading
char packetId = packet.readGChar(); // Reads byte - 32
int value = packet.readGInt(); // Reads 3-byte Graal int
Common Patterns
Sending Packets to Players
// To one player
player->sendPacket(packet);
// To all players in a level
server->sendPacketToOneLevel(packet, level);
// To all players
server->sendPacketToAll(packet);
File Sending
Large files are sent in chunks:
PLO_LARGEFILESTART - filename
PLO_LARGEFILESIZE - total size
PLO_RAWDATA + file chunks
PLO_LARGEFILEEND - signals completion
Board Modifications
Board data is always 8192 bytes (64x64 tiles, 2 bytes each):
// Server sends board packet
sendPacket(CString() >> (char)PLO_BOARDPACKET << boardData);
// Board modifications include position and size
packet >> (char)PLO_BOARDMODIFY >> (char)x >> (char)y
>> (char)width >> (char)height << tileData;
Coordinate Systems
Tile vs Pixel Coordinates
Tiles: 0-63 range, used for board positions
Pixels: Tiles * 16, used for precise positioning
Wire format: Often pixels/2 or pixels/8 depending on packet
GMAP Coordinates
Local: Position within current 64x64 segment
World:
gmaplevelx * 64 + localx(in tiles)X2/Y2: Always in pixels, can be negative (encoded with bit flag)
Property Encoding
Numeric Properties
Most use GCHAR encoding (value + 32)
Larger values use GINT (3 bytes) or GINT5 (5 bytes)
Special offsets: Z property has +25 offset
String Properties
Length-prefixed with GCHAR
Maximum 223 characters
Common: nickname, chat, level name, guild
Special Encodings
Colors: 5 consecutive GCHARs
Sprite: Encoded with direction in lower 2 bits
AP: Alignment points, 100 units = full alignment
Error Handling
Disconnect Messages
sendPacket(CString() >> (char)PLO_DISCMESSAGE << reason);
Always followed by connection termination.
File Send Failures
sendPacket(CString() >> (char)PLO_FILESENDFAILED << filename);
Filename reads to end of packet.
Important Notes
Packet Boundaries: Each packet is self-contained. “Read to end” means read to the end of the current packet, not the stream.
Null Characters: Level names may contain nulls. The server doesn’t strip them, but clients should handle them appropriately for display.
GMAP Mode: When on a GMAP, PLO_LEVELNAME sends the GMAP name, not the segment name. Segment info comes from GMAP file parsing.
Version Differences: Some behaviors change based on client version (e.g., GANI format, encryption type).
Quick Reference Table
Packet |
String Type |
Notes |
|---|---|---|
PLI_LEVELWARP |
Read to end |
Level name after X,Y |
PLO_LEVELNAME |
Read to end |
Entire packet is name |
PLO_LEVELLINK |
Newline-terminated |
Link data as single string |
PLO_PRIVATEMESSAGE |
GSTRING |
Player ID, then length+message |
PLO_FILESENDFAILED |
Read to end |
Filename |
Player properties |
GSTRING |
Length-prefixed strings |
NPC properties |
GSTRING |
Length-prefixed strings |