Skip to content

Game

Once Mason Service sends you your server details, the following is all that’s needed to make a simple connection to the server:

mason.on("partyJoinServer", (server: ApiServer) => {
const game = new Game(server, { displayName: "Example Bot" });
});

There are some more options which you can tweak to fit your needs:

NAMETYPEDESCRIPTIONDEFAULT
displayNamestringBot’s in-game name”Player”
proxyAgentConnection proxyundefined
schemasbooleanAutomatically parse JSON data and emit Schema eventstrue
rpcMappingDumpedDataRPC mapping for the targetted platform & version*built-in mapping**
udpbooleanCommunicate over UDP where possiblefalse
autoAckTickbooleanAutomatically acknowledge UDP tickstrue
sslbooleanConnect via WSStrue

* Dumping RPC mappings is not easy and there is no public tool that can do it for you. In case you need any help, feel free to ask us in our Discord server.
** Built-in mapping version can be checked in zombslib source code (rpcs.json).

After initializing a connection, you must wait for the server to accept your join request and send you an EnterWorldResponse.

game.on("EnterWorldResponse", (response: EnterWorldResponse) => {
if (response.allowed) {
game.setPlatformRpc(/* your platform, same as in your RPC mapping */);
game.startTcpStreamRpc(0, 0);
}
});

The snippet above does two important things:

  • First, it sends a SetPlatformRpc to the server, which is required before anything else can be done. Not sure why they designed it that way but whatever.
  • Then, StartTcpStreamRpc is sent which informs the server that you are ready to start receiving entity updates.

Just like with Mason Service, zombslib handles all decoding/encoding for you and exposes a user-friendly API. Here’s a couple very simple examples:

In this example, every tick (servers run at 64 tps) all the newly created entities, meaning ones which have just appeared in bot’s FOV, are printed to the console.

game.on("EntityUpdate", (u: EntityUpdate) => {
console.log(u.createdEntities?.flatMap((uid) => game.getEntityByUid(uid)));
});

Here, the bot repeats every chat message it sees (as long as it didn’t come from the bot itself).

game.on("ReceiveChatMessageRpc", (rpc: ReceiveChatMessageRpc) => {
if (rpc.uid !== game.getMyUid()) {
game.sendChatMessageRpc("Local", rpc.message);
}
});
  • getEntityList(): Map<number, NetworkEntity>
    Retrives all entities, maps them to their uids.

  • getEntitiesByType(type: EntityType): Map<number, NetworkEntity>
    Retrives entities of given type, maps them to their uids.

  • getEntityByUid(uid: number): NetworkEntity?
    Retrives entity with given uid if it exists.

  • getPlayerByName(name: string): NetworkEntity?
    Retrives player entity with given uid if it exists.

  • getSelf(): NetworkEntity?
    Retrives controlled bot entity (“local player”) if it exists (it should).

  • getSelfUid(): number
    Retrives controlled bot’s uid.

  • toWorldPos(pos: Vector2): Vector2
    Translates server position to world (Unity) position.

  • toServerPos(pos: Vector2): Vector2
    Translates Unity position to server position.

  • getEnterWorldResponse(): EnterWorldResponse
    Retrives lobby join response.

  • shutdown(): void
    Disconnects the bot.

  • send(data: Uint8Array, udp: boolean): void
    Sends raw data to the server. Used internally.

All OutRpc’s got their own method and should be pretty straightforward to understand.

Notes:

  • The few RPC’s with more than 3 fields are passed as entire objects instead of as separate function args.
  • InputRpc can be a partial.
  • Due to SetSkinRpc being recognized as an “array rpc”, its corresponding method is defined as setSkinRpc(rpcs: SetSkinRpc[]).
  • RPC’s
    Event name: "ExampleRpc"
    Parameters: (rpc: ExampleRpc, extra: RpcExtra)

    // interface RpcExtra {
    // tick?: number;
    // udpCookie?: number;
    // transport: "tcp" | "udp";
    // }
    game.on("KillFeedRpc", (rpc: KillFeedRpc, extra: RpcExtra) => ...);
  • RPC’s (any)
    Event name: "Rpc"
    Parameters: (name: string, rpc: object, extra: RpcExtra)

    // This will trigger on any RPC. Let's say that you receive an AirDropRpc:
    // name => "AirDropRpc"
    // rpc => type AirDropRpc
    game.on("Rpc", (name: string, rpc: object, extra: RpcExtra) => ...);
  • Schemas
    Event name: "SchemaExample"
    Parameters: (data: ExampleRpc) or (data: SchemaExample[]) (majority)

    game.on("SchemaGeneral", (data: SchemaGeneral) => ...);
    game.on("SchemaWeapons", (data: SchemaWeapon[]) => ...);
  • Any packet
    Event name: "RawData"
    Parameters: (data: Uint8Array, transport: "tpc" | "udp", packetId: PacketId)

  • Entity update
    Event name: "EntityUpdate"
    Parameters (TCP): (data: EntityUpdate, packetId: PacketId)
    Parameters (UDP): (data: UdpTick, packetId: PacketId)

  • Lobby join response
    Event name: "EnterWorldResponse"
    Parameters: (response: EnterWorldResponse)

  • UDP connect response
    Event name: "UdpConnectResponse"
    Parameters: (response: UdpConnectResponse)

EntityType enum:

NAMEIDNOTES
ZombieEntity0x0ed7fd27
SprayEntity0x1e1837cc
PortalEntity0x2293598d
PlayerEntity0x4254ae62Includes bots
CrystalEntity0x58eafdbdCrystal Clash crystals
VehicleEntity0x5d0b456be.g. Hoverboard
PhysicsEntity0x8e187e23Likely unused
PlaneEntity0x8fe5d35b
ItemEntity0xa7ecd754
ProjectileEntity0xb6cebbaa
BuildingEntity0xdf853d95
PropEntity0xecaa7004e.g. Bush
GasEntity0xf15cdbb8The zone
NpcEntity0xf4de4be0No longer in use
UnknownEntity0xf5cf683eNo idea
PlayerBuildingEntity0xf63a37d6e.g. Snow Wall