#include #include #include "types.h" #include "data.h" #include "bss.h" #include "lib/main.h" #include "lib/mtx.h" #include "lib/model.h" #include "game/mplayer/mplayer.h" #include "game/chr.h" #include "game/chraction.h" #include "game/prop.h" #include "game/propobj.h" #include "game/player.h" #include "game/playermgr.h" #include "game/bondgun.h" #include "game/game_0b0fd0.h" #include "game/inv.h" #include "game/menu.h" #include "game/setup.h" #include "game/setuputils.h" #include "game/modelmgr.h" #include "game/propsnd.h" #include "system.h" #include "console.h" #include "net/net.h" #include "net/netbuf.h" #include "net/netmsg.h" extern u64 g_RngSeed; extern u64 g_Rng2Seed; /* utils */ static inline u32 netbufWriteRooms(struct netbuf *buf, const s16 *rooms, const s32 num) { for (s32 i = 0; i < num; ++i) { netbufWriteS16(buf, rooms[i]); if (rooms[i] < 0) { break; } } return buf->error; } static inline u32 netbufReadRooms(struct netbuf *buf, s16 *rooms, const s32 num) { for (s32 i = 0; i < num; ++i) { rooms[i] = netbufReadS16(buf); if (rooms[i] < 0) { break; } } return buf->error; } static inline u32 netbufWriteGset(struct netbuf *buf, const struct gset *gset) { netbufWriteData(buf, gset, sizeof(*gset)); return buf->error; } static inline u32 netbufReadGset(struct netbuf *buf, struct gset *gset) { netbufReadData(buf, gset, sizeof(*gset)); return buf->error; } static inline u32 netbufWritePlayerMove(struct netbuf *buf, const struct netplayermove *in) { netbufWriteU32(buf, in->tick); netbufWriteU32(buf, in->ucmd); netbufWriteF32(buf, in->leanofs); netbufWriteF32(buf, in->crouchofs); netbufWriteF32(buf, in->movespeed[0]); netbufWriteF32(buf, in->movespeed[1]); netbufWriteF32(buf, in->angles[0]); netbufWriteF32(buf, in->angles[1]); netbufWriteF32(buf, in->crosspos[0]); netbufWriteF32(buf, in->crosspos[1]); netbufWriteS8(buf, in->weaponnum); netbufWriteCoord(buf, &in->pos); return buf->error; } static inline u32 netbufReadPlayerMove(struct netbuf *buf, struct netplayermove *in) { in->tick = netbufReadU32(buf); in->ucmd = netbufReadU32(buf); in->leanofs = netbufReadF32(buf); in->crouchofs = netbufReadF32(buf); in->movespeed[0] = netbufReadF32(buf); in->movespeed[1] = netbufReadF32(buf); in->angles[0] = netbufReadF32(buf); in->angles[1] = netbufReadF32(buf); in->crosspos[0] = netbufReadF32(buf); in->crosspos[1] = netbufReadF32(buf); in->weaponnum = netbufReadS8(buf); netbufReadCoord(buf, &in->pos); return buf->error; } static inline u32 netbufWritePropPtr(struct netbuf *buf, const struct prop *prop) { netbufWriteU32(buf, prop ? prop->syncid : 0); return buf->error; } static inline struct prop *netbufReadPropPtr(struct netbuf *buf) { const u32 syncid = netbufReadU32(buf); if (syncid == 0) { return NULL; } // TODO: make a map or something for (s32 i = 0; i < g_Vars.maxprops; ++i) { if (g_Vars.props[i].syncid == syncid) { return &g_Vars.props[i]; } } sysLogPrintf(LOG_WARNING, "NET: prop with syncid %u does not exist", syncid); return NULL; } static inline s32 propRoomsEqual(const RoomNum *ra, const RoomNum *rb) { for (s32 i = 0; i < 8; ++i) { if (ra[i] != rb[i]) { return 0; } if (ra[i] == -1) { break; } } return 1; } /* client -> server */ u32 netmsgClcAuthWrite(struct netbuf *dst) { netbufWriteU8(dst, CLC_AUTH); netbufWriteU8(dst, 1); // TODO: number of local players netbufWriteU8(dst, g_NetLocalClient->settings.bodynum); netbufWriteU8(dst, g_NetLocalClient->settings.headnum); netbufWriteStr(dst, g_NetLocalClient->settings.name); return dst->error; } u32 netmsgClcAuthRead(struct netbuf *src, struct netclient *srccl) { if (srccl->state != CLSTATE_AUTH) { sysLogPrintf(LOG_WARNING, "NET: CLC_AUTH from %s, who is not in CLSTATE_AUTH", netFormatClientAddr(srccl)); return 1; } const u8 players = netbufReadU8(src); const u8 bodynum = netbufReadU8(src); const u8 headnum = netbufReadU8(src); char *name = netbufReadStr(src); if (src->error) { sysLogPrintf(LOG_WARNING, "NET: malformed CLC_AUTH from %s", netFormatClientAddr(srccl)); return 1; } strncpy(srccl->settings.name, name, sizeof(srccl->settings.name) - 1); srccl->settings.bodynum = bodynum; srccl->settings.headnum = headnum; srccl->state = CLSTATE_LOBBY; sysLogPrintf(LOG_NOTE, "NET: CLC_AUTH from %s (%s), responding", netFormatClientAddr(srccl), srccl->settings.name); netbufStartWrite(&srccl->out); netmsgSvcAuthWrite(&srccl->out, srccl); netSend(srccl, NULL, true); sysLogPrintf(LOG_CHAT, "NET: %s (client %u) joined", srccl->settings.name, srccl->id); return 0; } u32 netmsgClcChatWrite(struct netbuf *dst, const char *str) { netbufWriteU8(dst, CLC_CHAT); netbufWriteStr(dst, str); return dst->error; } u32 netmsgClcChatRead(struct netbuf *src, struct netclient *srccl) { char tmp[1024]; const char *msg = netbufReadStr(src); if (msg && !src->error) { sysLogPrintf(LOG_CHAT, msg); netbufStartWrite(&g_NetMsgRel); netmsgSvcChatWrite(&g_NetMsgRel, msg); netSend(NULL, &g_NetMsgRel, true); } return src->error; } u32 netmsgClcMoveWrite(struct netbuf *dst) { netbufWriteU8(dst, CLC_MOVE); netbufWriteU32(dst, g_NetLocalClient->inmove[0].tick); netbufWritePlayerMove(dst, &g_NetLocalClient->outmove[0]); return dst->error; } u32 netmsgClcMoveRead(struct netbuf *src, struct netclient *srccl) { struct netplayermove newmove; const u32 outmoveack = netbufReadU32(src); netbufReadPlayerMove(src, &newmove); if (srccl->state != CLSTATE_GAME) { // silently ignore return src->error; } srccl->outmoveack = outmoveack; if (!src->error) { // enforce teleports and such if (srccl->forcetick && srccl->player) { if (srccl->outmoveack >= srccl->forcetick) { // client has acknowledged our last sent move, clear the force flags srccl->forcetick = 0; srccl->player->ucmd &= ~UCMD_FL_FORCEMASK; sysLogPrintf(LOG_NOTE, "NET: client %u successfully forcemoved", srccl->id); } else { // client hasn't teleported yet, discard the new position from the input command if (srccl->player->ucmd & UCMD_FL_FORCEPOS) { newmove.pos = srccl->player->prop->pos; } if (srccl->player->ucmd & UCMD_FL_FORCEANGLE) { newmove.angles[0] = srccl->player->vv_theta; newmove.angles[1] = srccl->player->vv_verta; } } } // make space in the move stack memmove(srccl->inmove + 1, srccl->inmove, sizeof(srccl->inmove) - sizeof(*srccl->inmove)); srccl->inmove[0] = newmove; srccl->lerpticks = 0; } return src->error; } /* server -> client */ u32 netmsgSvcAuthWrite(struct netbuf *dst, struct netclient *authcl) { netbufWriteU8(dst, SVC_AUTH); netbufWriteU8(dst, authcl - g_NetClients); netbufWriteU8(dst, g_NetMaxClients); netbufWriteU32(dst, g_NetTick); return dst->error; } u32 netmsgSvcAuthRead(struct netbuf *src, struct netclient *srccl) { if (g_NetLocalClient->state != CLSTATE_AUTH) { sysLogPrintf(LOG_WARNING, "NET: SVC_AUTH from server but we're not in AUTH state"); return 1; } const u8 id = netbufReadU8(src); const u8 maxclients = netbufReadU8(src); g_NetTick = netbufReadU32(src); if (g_NetLocalClient->in.error || id == NET_NULL_CLIENT || id == 0 || maxclients == 0) { sysLogPrintf(LOG_WARNING, "NET: malformed SVC_AUTH from server"); return 1; } sysLogPrintf(LOG_NOTE, "NET: SVC_AUTH from server, our ID is %u", id); // there's at least one client, which is us, and we know maxclients as well g_NetMaxClients = maxclients; g_NetNumClients = 1; // we now know our proper ID, so move to the appropriate client slot and reset the old one g_NetLocalClient = &g_NetClients[id]; g_NetClients[id] = g_NetClients[NET_MAX_CLIENTS]; g_NetLocalClient->out.data = g_NetLocalClient->out_data; g_NetLocalClient->id = id; // clear out the old slot g_NetClients[NET_MAX_CLIENTS].id = NET_MAX_CLIENTS; g_NetClients[NET_MAX_CLIENTS].state = 0; g_NetClients[NET_MAX_CLIENTS].peer = NULL; // the server's client probably is in the lobby state by now g_NetClients[0].state = CLSTATE_LOBBY; g_NetLocalClient->state = CLSTATE_LOBBY; return src->error; } u32 netmsgSvcChatWrite(struct netbuf *dst, const char *str) { netbufWriteU8(dst, SVC_CHAT); netbufWriteStr(dst, str); return dst->error; } u32 netmsgSvcChatRead(struct netbuf *src, struct netclient *srccl) { char tmp[1024]; const char *msg = netbufReadStr(src); if (msg && !src->error) { sysLogPrintf(LOG_CHAT, msg); } return src->error; } u32 netmsgSvcStageStartWrite(struct netbuf *dst) { netbufWriteU8(dst, SVC_STAGE_START); netbufWriteU32(dst, g_NetTick); netbufWriteU64(dst, g_RngSeed); netbufWriteU64(dst, g_Rng2Seed); netbufWriteU8(dst, g_StageNum); if (g_StageNum == STAGE_TITLE || g_StageNum == STAGE_CITRAINING) { // going back to lobby, don't need anything else return dst->error; } // game settings netbufWriteU8(dst, 0); // 0 for combat sim TODO: coop/anti netbufWriteU8(dst, g_MpSetup.scenario); netbufWriteU8(dst, g_MpSetup.scorelimit); netbufWriteU8(dst, g_MpSetup.timelimit); netbufWriteU16(dst, g_MpSetup.teamscorelimit); netbufWriteU16(dst, g_MpSetup.chrslots); netbufWriteU32(dst, g_MpSetup.options); netbufWriteData(dst, g_MpSetup.weapons, sizeof(g_MpSetup.weapons)); // who the fuck is in the game netbufWriteU8(dst, g_NetNumClients); for (s32 i = 0; i < g_NetMaxClients; ++i) { struct netclient *ncl = &g_NetClients[i]; if (ncl->state) { netbufWriteU8(dst, ncl->id); netbufWriteU8(dst, ncl->settings.bodynum); netbufWriteU8(dst, ncl->settings.headnum); netbufWriteStr(dst, ncl->settings.name); memset(ncl->inmove, 0, sizeof(ncl->inmove)); memset(ncl->outmove, 0, sizeof(ncl->outmove)); ncl->lerpticks = 0; ncl->outmoveack = 0; ncl->state = CLSTATE_GAME; } } return dst->error; } u32 netmsgSvcStageStartRead(struct netbuf *src, struct netclient *srccl) { if (srccl->state != CLSTATE_LOBBY) { sysLogPrintf(LOG_WARNING, "NET: SVC_STAGE from server but we're not in LOBBY state"); return 1; } g_NetTick = netbufReadU32(src); g_RngSeed = netbufReadU64(src); g_Rng2Seed = netbufReadU64(src); const u8 stagenum = netbufReadU8(src); if (stagenum == STAGE_TITLE || stagenum == STAGE_CITRAINING) { // server went back to lobby, we don't really care return src->error; } const u8 mode = netbufReadU8(src); // TODO: coop, anti g_MpSetup.stagenum = stagenum; g_MpSetup.scenario = netbufReadU8(src); g_MpSetup.scorelimit = netbufReadU8(src); g_MpSetup.timelimit = netbufReadU8(src); g_MpSetup.teamscorelimit = netbufReadU16(src); g_MpSetup.chrslots = netbufReadU16(src); g_MpSetup.options = netbufReadU32(src); netbufReadData(src, g_MpSetup.weapons, sizeof(g_MpSetup.weapons)); strcpy(g_MpSetup.name, "server"); if (src->error) { sysLogPrintf(LOG_WARNING, "NET: malformed SVC_STAGE from server"); return 1; } // read players const u8 numplayers = netbufReadU8(src); if (src->error || !numplayers || numplayers > g_NetMaxClients + 1) { sysLogPrintf(LOG_WARNING, "NET: malformed SVC_STAGE from server"); return 2; } for (u8 i = 0; i < numplayers; ++i) { const u8 id = netbufReadU8(src); struct netclient *ncl = &g_NetClients[id]; if (ncl != g_NetLocalClient) { ncl->id = id; ncl->settings.bodynum = netbufReadU8(src); ncl->settings.headnum = netbufReadU8(src); char *name = netbufReadStr(src); if (name) { strncpy(ncl->settings.name, name, sizeof(ncl->settings.name) - 1); } else { sysLogPrintf(LOG_WARNING, "NET: malformed SVC_STAGE from server"); return 3; } } else { // skip our own settings netbufReadU8(src); netbufReadU8(src); netbufReadStr(src); } ncl->state = CLSTATE_GAME; ncl->player = NULL; } if (src->error) { return src->error; } g_NetNumClients = numplayers; sysLogPrintf(LOG_NOTE, "NET: SVC_STAGE from server: going to stage 0x%02x with %u players", g_MpSetup.stagenum, numplayers); mpStartMatch(); menuStop(); return 0; } u32 netmsgSvcStageEndWrite(struct netbuf *dst) { netbufWriteU8(dst, SVC_STAGE_END); for (s32 i = 0; i < g_NetMaxClients; ++i) { struct netclient *ncl = &g_NetClients[i]; if (ncl->state) { ncl->state = CLSTATE_LOBBY; ncl->playernum = 0; if (ncl->player) { ncl->player->client = NULL; ncl->player->isremote = false; ncl->player = NULL; } if (ncl->config) { ncl->config->client = NULL; ncl->config = NULL; } } } return dst->error; } u32 netmsgSvcStageEndRead(struct netbuf *src, struct netclient *srccl) { for (s32 i = 0; i < g_NetMaxClients; ++i) { struct netclient *ncl = &g_NetClients[i]; if (ncl->state) { ncl->state = CLSTATE_LOBBY; ncl->player = NULL; ncl->config = NULL; } } mainEndStage(); return src->error; } u32 netmsgSvcPlayerMoveWrite(struct netbuf *dst, struct netclient *movecl) { if (movecl->state < CLSTATE_GAME || !movecl->player || !movecl->player->prop) { return dst->error; } netbufWriteU8(dst, SVC_PLAYER_MOVE); netbufWriteU8(dst, movecl->id); netbufWriteU32(dst, movecl->inmove[0].tick); netbufWritePlayerMove(dst, &movecl->outmove[0]); if (movecl->outmove[0].ucmd & UCMD_FL_FORCEMASK) { netbufWriteRooms(dst, movecl->player->prop->rooms, ARRAYCOUNT(movecl->player->prop->rooms)); } return dst->error; } u32 netmsgSvcPlayerMoveRead(struct netbuf *src, struct netclient *srccl) { if (srccl->state != CLSTATE_GAME) { sysLogPrintf(LOG_WARNING, "NET: SVC_PLAYER_MOVE from server but we're not in GAME state"); return 1; } u8 id = 0; u32 outmoveack = 0; struct netplayermove newmove; RoomNum newrooms[8] = { -1 }; id = netbufReadU8(src); outmoveack = netbufReadU32(src); netbufReadPlayerMove(src, &newmove); if (newmove.ucmd & UCMD_FL_FORCEMASK) { netbufReadRooms(src, newrooms, ARRAYCOUNT(newrooms)); } if (src->error) { return src->error; } struct netclient *movecl = &g_NetClients[id]; // make space in the move stack memmove(movecl->inmove + 1, movecl->inmove, sizeof(movecl->inmove) - sizeof(*movecl->inmove)); movecl->inmove[0] = newmove; movecl->outmoveack = outmoveack; movecl->lerpticks = 0; if (movecl == g_NetLocalClient && (newmove.ucmd & UCMD_FL_FORCEMASK)) { // server wants to teleport us if (movecl->player && movecl->player->prop) { chrSetPos(movecl->player->prop->chr, &newmove.pos, newrooms, newmove.angles[0], (newmove.ucmd & UCMD_FL_FORCEGROUND) != 0); } } return src->error; } u32 netmsgSvcPlayerStatsWrite(struct netbuf *dst, struct netclient *actcl) { if (actcl->state < CLSTATE_GAME || !actcl->player || !actcl->player->prop) { return dst->error; } const struct player *pl = actcl->player; const u8 flags = (pl->isdead != 0) | (pl->gunctrl.dualwielding << 1) | (pl->hands[0].inuse << 2) | (pl->hands[1].inuse << 3); netbufWriteU8(dst, SVC_PLAYER_STATS); netbufWriteU8(dst, actcl->id); netbufWriteU8(dst, flags); netbufWriteS8(dst, pl->gunctrl.weaponnum); netbufWriteF32(dst, pl->prop->chr->damage); netbufWriteF32(dst, pl->bondhealth); netbufWriteF32(dst, pl->prop->chr->cshield); netbufWriteCoord(dst, &pl->bondshotspeed); for (s32 i = 0; i < 2; ++i) { if (pl->hands[i].inuse) { netbufWriteS16(dst, pl->hands[i].loadedammo[0]); netbufWriteS16(dst, pl->hands[i].loadedammo[1]); } } // assemble a bitmask of all non-zero ammo entries below 32 u32 mask = 0; for (u32 i = 0; i < 32; ++i) { if (pl->ammoheldarr[i]) { mask |= (1 << i); } } netbufWriteU32(dst, mask); // write all entries that are non-zero or have index above 32 for (s32 i = 0; i < ARRAYCOUNT(pl->ammoheldarr); ++i) { if (pl->ammoheldarr[i] || i >= 32) { netbufWriteS16(dst, pl->ammoheldarr[i]); } } return dst->error; } u32 netmsgSvcPlayerStatsRead(struct netbuf *src, struct netclient *srccl) { const u8 clid = netbufReadU8(src); const u8 flags = netbufReadU8(src); const s8 newweaponnum = netbufReadS8(src); const f32 newdamage = netbufReadF32(src); const f32 newhealth = netbufReadF32(src); const f32 newshield = netbufReadF32(src); struct coord newshotspeed; netbufReadCoord(src, &newshotspeed); const bool handused[2] = { (flags & (1 << 2)) != 0, (flags & (1 << 3)) != 0 }; if (src->error) { return src->error; } struct netclient *actcl = g_NetClients + clid; struct player *pl = actcl->player; if (!pl || !pl->prop) { return src->error; } pl->prop->chr->damage = newdamage; pl->prop->chr->cshield = newshield; pl->bondhealth = newhealth; pl->bondshotspeed = newshotspeed; for (s32 i = 0; i < 2; ++i) { if (handused[i]) { pl->hands[i].loadedammo[0] = netbufReadS16(src); pl->hands[i].loadedammo[1] = netbufReadS16(src); } } const u32 ammomask = netbufReadU32(src); for (s32 i = 0; i < ARRAYCOUNT(pl->ammoheldarr); ++i) { if (i >= 32 || (ammomask & (1 << i))) { pl->ammoheldarr[i] = netbufReadS16(src); } else { pl->ammoheldarr[i] = 0; } } const s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(actcl->playernum); const bool newisdead = (flags & (1 << 0)) != 0; if (!pl->isdead && newisdead) { s16 shooter; if (pl->prop->chr->lastshooter >= 0 && pl->prop->chr->timeshooter > 0) { shooter = pl->prop->chr->lastshooter; } else { shooter = g_Vars.currentplayernum; } playerDieByShooter(shooter, true); } else if (pl->isdead && !newisdead) { playerStartNewLife(); } const bool dualwielding = (flags & (1 << 1)) != 0; if (!pl->isdead && (newweaponnum != pl->gunctrl.weaponnum || dualwielding != pl->gunctrl.dualwielding)) { bgunEquipWeapon(newweaponnum); pl->gunctrl.dualwielding = dualwielding; } setCurrentPlayerNum(prevplayernum); return src->error; } u32 netmsgSvcPropMoveWrite(struct netbuf *dst, struct prop *prop, struct coord *initrot) { u8 flags = (prop->obj != NULL); struct projectile *projectile = NULL; if (prop->obj) { if (prop->obj->hidden & OBJHFLAG_EMBEDDED) { projectile = prop->obj->embedment->projectile; } else if (prop->obj->hidden & OBJHFLAG_PROJECTILE) { projectile = prop->obj->projectile; } if (projectile) { flags |= (1 << 1); if (initrot) { flags |= (1 << 2); } if (prop->obj->type == OBJTYPE_HOVERPROP || prop->obj->type == OBJTYPE_HOVERBIKE) { flags |= (1 << 3); } } } netbufWriteU8(dst, SVC_PROP_MOVE); netbufWriteU8(dst, flags); netbufWritePropPtr(dst, prop); netbufWriteCoord(dst, &prop->pos); netbufWriteRooms(dst, prop->rooms, ARRAYCOUNT(prop->rooms)); if (projectile) { netbufWriteCoord(dst, &projectile->speed); netbufWriteF32(dst, projectile->unk0dc); netbufWriteU32(dst, projectile->flags); netbufWriteS8(dst, projectile->bouncecount); netbufWritePropPtr(dst, projectile->ownerprop); if (initrot) { netbufWriteCoord(dst, initrot); } if (prop->obj->type == OBJTYPE_HOVERPROP || prop->obj->type == OBJTYPE_HOVERBIKE) { netbufWriteF32(dst, projectile->unk08c); netbufWriteF32(dst, projectile->unk098); netbufWriteF32(dst, projectile->unk0e0); netbufWriteF32(dst, projectile->unk0e4); netbufWriteF32(dst, projectile->unk0ec); netbufWriteF32(dst, projectile->unk0f0); } } return dst->error; } u32 netmsgSvcPropMoveRead(struct netbuf *src, struct netclient *srccl) { const u8 flags = netbufReadU8(src); struct prop *prop = netbufReadPropPtr(src); struct coord pos; netbufReadCoord(src, &pos); RoomNum rooms[8] = { -1 }; netbufReadRooms(src, rooms, ARRAYCOUNT(rooms)); if (src->error || !prop) { return src->error; } prop->pos = pos; if (!propRoomsEqual(rooms, prop->rooms)) { if (prop->active) { propDeregisterRooms(prop); } roomsCopy(rooms, prop->rooms); if (prop->active) { propRegisterRooms(prop); } } if (!(flags & (1 << 0))) { return src->error; } if (!prop->obj) { sysLogPrintf(LOG_WARNING, "NET: SVC_PROP_MOVE: prop %u should have an obj, but doesn't", prop->syncid); return 1; } if (!(flags & (1 << 1))) { return src->error; } // create a projectile for this prop if it isn't already there func0f0685e4(prop); struct projectile *projectile = NULL; if (prop->obj->hidden & OBJHFLAG_EMBEDDED) { projectile = prop->obj->embedment->projectile; } else if (prop->obj->hidden & OBJHFLAG_PROJECTILE) { projectile = prop->obj->projectile; } if (!projectile) { sysLogPrintf(LOG_WARNING, "NET: SVC_PROP_MOVE: prop %u should have a projectile, but doesn't", prop->syncid); return 1; } netbufReadCoord(src, &projectile->speed); projectile->unk0dc = netbufReadF32(src); projectile->flags = netbufReadU32(src); projectile->bouncecount = netbufReadS8(src); projectile->ownerprop = netbufReadPropPtr(src); if (flags & (1 << 2)) { struct coord initrot; netbufReadCoord(src, &initrot); mtx4LoadRotation(&initrot, &projectile->mtx); } if (flags & (1 << 3)) { projectile->unk08c = netbufReadF32(src); projectile->unk098 = netbufReadF32(src); projectile->unk0e0 = netbufReadF32(src); projectile->unk0e4 = netbufReadF32(src); projectile->unk0ec = netbufReadF32(src); projectile->unk0f0 = netbufReadF32(src); } return src->error; } u32 netmsgSvcPropSpawnWrite(struct netbuf *dst, struct prop *prop) { const u8 msgflags = (prop->active != 0) | ((prop->obj != NULL) << 1) | ((prop->forcetick != 0) << 2); const u8 objtype = prop->obj ? prop->obj->type : 0; netbufWriteU8(dst, SVC_PROP_SPAWN); netbufWriteU8(dst, msgflags); netbufWriteU32(dst, prop->syncid); netbufWriteCoord(dst, &prop->pos); netbufWriteRooms(dst, prop->rooms, ARRAYCOUNT(prop->rooms)); netbufWritePropPtr(dst, prop->parent); netbufWriteU8(dst, prop->type); netbufWriteU8(dst, objtype); netbufWriteU8(dst, prop->flags); switch (prop->type) { case PROPTYPE_WEAPON: // dropped gun or projectile netbufWriteS16(dst, prop->weapon->base.modelnum); netbufWriteU8(dst, prop->weapon->weaponnum); netbufWriteS8(dst, prop->weapon->unk5d); netbufWriteS8(dst, prop->weapon->unk5e); netbufWriteU8(dst, prop->weapon->gunfunc); netbufWriteS16(dst, prop->weapon->timer240); break; case PROPTYPE_OBJ: // we already send most of the important obj stuff below, so netbufWriteS16(dst, prop->obj->modelnum); if (objtype == OBJTYPE_AUTOGUN) { // thrown laptop probably struct autogunobj *autogun = (struct autogunobj *)prop->obj; const u8 ownerplayernum = (prop->obj->hidden & 0xf0000000) >> 28; netbufWriteU8(dst, autogun->ammoquantity); netbufWriteU8(dst, autogun->firecount); netbufWriteU8(dst, g_Vars.players[ownerplayernum]->client->id); } break; default: break; } if (prop->obj) { netbufWriteU32(dst, prop->obj->flags); netbufWriteU32(dst, prop->obj->flags2); netbufWriteU32(dst, prop->obj->flags3); netbufWriteU32(dst, prop->obj->hidden); netbufWriteU8(dst, prop->obj->hidden2); netbufWriteU16(dst, prop->obj->extrascale); for (s32 i = 0; i < 3; ++i) { for (s32 j = 0; j < 3; ++j) { netbufWriteF32(dst, prop->obj->realrot[i][j]); } } if ((prop->obj->hidden & OBJHFLAG_PROJECTILE) && prop->obj->projectile) { netbufWritePropPtr(dst, prop->obj->projectile->ownerprop); netbufWriteU32(dst, prop->obj->projectile->flags); netbufWriteS32(dst, prop->obj->projectile->pickuptimer240); netbufWriteF32(dst, prop->obj->projectile->unk08c); netbufWriteS16(dst, prop->obj->projectile->droptype); netbufWriteMtxf(dst, &prop->obj->projectile->mtx); } } return dst->error; } u32 netmsgSvcPropSpawnRead(struct netbuf *src, struct netclient *srccl) { const u8 msgflags = netbufReadU8(src); const u32 syncid = netbufReadU32(src); struct coord pos; netbufReadCoord(src, &pos); RoomNum rooms[8] = { -1 }; netbufReadRooms(src, rooms, ARRAYCOUNT(rooms)); struct prop *parent = netbufReadPropPtr(src); const u8 type = netbufReadU8(src); const u8 objtype = netbufReadU8(src); const u8 propflags = netbufReadU8(src); if (src->error) { return src->error; } struct prop *prop = (type == PROPTYPE_OBJ && objtype == OBJTYPE_AUTOGUN) ? NULL : propAllocate(); if (type == PROPTYPE_WEAPON) { const s16 modelnum = netbufReadS16(src); const u8 weaponnum = netbufReadU8(src); const s8 unk5d = netbufReadS8(src); const s8 unk5e = netbufReadS8(src); const u8 gunfunc = netbufReadU8(src); const s16 timer240 = netbufReadS16(src); setupLoadModeldef(modelnum); struct modeldef *modeldef = g_ModelStates[modelnum].modeldef; struct model *model = modelmgrInstantiateModelWithoutAnim(modeldef); struct weaponobj *weapon = weaponCreate(prop == NULL, model == NULL, modeldef); struct weaponobj tmp = { 256, // extrascale 0, // hidden2 OBJTYPE_WEAPON, // type 0, // modelnum -1, // pad OBJFLAG_FALL, // flags 0, // flags2 0, // flags3 NULL, // prop NULL, // model 1, 0, 0, // realrot 0, 1, 0, 0, 0, 1, 0, // hidden NULL, // geo NULL, // projectile 0, // damage 1000, // maxdamage 0xff, 0xff, 0xff, 0x00, // shadecol 0xff, 0xff, 0xff, 0x00, // nextcol 0x0fff, // floorcol 0, // tiles 0, // weaponnum 0, // unk5d 0, // unk5e 0, // gunfunc 0, // fadeouttimer60 -1, // dualweaponnum -1, // timer240 NULL, // dualweapon }; *weapon = tmp; weapon->base.modelnum = modelnum; weapon->weaponnum = weaponnum; weapon->unk5d = unk5d; weapon->unk5e = unk5e; weapon->gunfunc = gunfunc; weapon->timer240 = timer240; prop = func0f08adc8(weapon, modeldef, prop, model); } else if (type == PROPTYPE_OBJ) { const s16 modelnum = netbufReadS16(src); if (objtype == OBJTYPE_AUTOGUN) { // thrown laptop? const u8 ammocount = netbufReadU8(src); const u8 firecount = netbufReadU8(src); const u8 clid = netbufReadU8(src); struct chrdata *ownerchr = g_NetClients[clid].player->prop->chr; struct autogunobj *obj = laptopDeploy(modelnum, NULL, ownerchr); prop = obj->base.prop; } } if (prop) { prop->type = type; prop->syncid = syncid; prop->pos = pos; prop->forcetick = (msgflags & (1 << 2)) != 0; // prop->flags = propflags; roomsCopy(rooms, prop->rooms); propRegisterRooms(prop); if (msgflags & (1 << 0)) { propActivate(prop); } else { propPause(prop); } if (propflags & PROPFLAG_ENABLED) { propEnable(prop); } else { propDisable(prop); } if (parent) { propReparent(prop, parent); } } else { sysLogPrintf(LOG_WARNING, "NET: no prop allocated when spawning prop %u (%u)", syncid, type); return src->error; } if (msgflags & (1 << 1)) { const u32 flags = netbufReadU32(src); const u32 flags2 = netbufReadU32(src); const u32 flags3 = netbufReadU32(src); u32 hidden = netbufReadU32(src); const u8 hidden2 = netbufReadU8(src); const u16 extrascale = netbufReadU16(src); for (s32 i = 0; i < 3; ++i) { for (s32 j = 0; j < 3; ++j) { prop->obj->realrot[i][j] = netbufReadF32(src); } } if (prop->obj) { if (hidden & OBJHFLAG_PROJECTILE) { func0f0685e4(prop); prop->obj->projectile->ownerprop = netbufReadPropPtr(src); prop->obj->projectile->flags = netbufReadU32(src); prop->obj->projectile->pickuptimer240 = netbufReadS32(src); prop->obj->projectile->unk08c = netbufReadF32(src); prop->obj->projectile->droptype = netbufReadS16(src); netbufReadMtxf(src, &prop->obj->projectile->mtx); if (prop->obj->projectile->flags & PROJECTILEFLAG_00000002) { // this is a thrown projectile, play throw sound psCreate(NULL, prop, SFX_THROW, -1, -1, 0, 0, PSTYPE_NONE, NULL, -1, NULL, -1, -1, -1, -1); } } // swap owner player numbers to match server u8 ownerplayernum = (hidden & 0xf0000000) >> 28; ownerplayernum = g_NetClients[ownerplayernum].playernum; hidden = (hidden & 0x0fffffff) | (ownerplayernum << 28); prop->obj->flags = flags; prop->obj->flags2 = flags2; prop->obj->flags3 = flags3; prop->obj->hidden = hidden; prop->obj->hidden2 = hidden2; prop->obj->extrascale = extrascale; if (prop->obj->model) { modelSetScale(prop->obj->model, prop->obj->model->scale * ((f32)extrascale / 256.f)); } } } return src->error; } u32 netmsgSvcPropDamageWrite(struct netbuf *dst, struct prop *prop, f32 damage, struct coord *pos, s32 weaponnum, s32 playernum) { if (!prop || !prop->obj) { return dst->error; } netbufWriteU8(dst, SVC_PROP_DAMAGE); netbufWritePropPtr(dst, prop); netbufWriteCoord(dst, pos); netbufWriteF32(dst, prop->obj->damage); netbufWriteF32(dst, damage); netbufWriteS8(dst, weaponnum); netbufWriteS8(dst, playernum); netbufWriteU32(dst, prop->obj->hidden); return dst->error; } u32 netmsgSvcPropDamageRead(struct netbuf *src, struct netclient *srccl) { struct prop *prop = netbufReadPropPtr(src); struct coord pos; netbufReadCoord(src, &pos); const f32 damagepre = netbufReadF32(src); const f32 damage = netbufReadF32(src); const s8 weaponnum = netbufReadS8(src); const s8 playernum = netbufReadS8(src); const u32 hidden = netbufReadU32(src); if (prop && prop->obj && prop->type != PROPTYPE_PLAYER && prop->type != PROPTYPE_CHR && !src->error) { prop->obj->damage = damagepre; prop->obj->hidden = hidden; objDamage(prop->obj, -damage, &pos, weaponnum, playernum); } return src->error; } u32 netmsgSvcPropPickupWrite(struct netbuf *dst, struct netclient *actcl, struct prop *prop, const s32 tickop) { netbufWriteU8(dst, SVC_PROP_PICKUP); netbufWriteU8(dst, actcl->id); netbufWriteS8(dst, tickop); netbufWritePropPtr(dst, prop); return dst->error; } u32 netmsgSvcPropPickupRead(struct netbuf *src, struct netclient *srccl) { const u8 clid = netbufReadU8(src); const s8 tickop = netbufReadS8(src); struct prop *prop = netbufReadPropPtr(src); if (src->error || !prop) { return 1; } struct netclient *actcl = g_NetClients + clid; const s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(actcl->playernum); propPickupByPlayer(prop, true); if (tickop != TICKOP_NONE) { propExecuteTickOperation(prop, tickop); } setCurrentPlayerNum(prevplayernum); return src->error; } u32 netmsgSvcPropUseWrite(struct netbuf *dst, struct prop *prop, struct netclient *usercl, const s32 tickop) { netbufWriteU8(dst, SVC_PROP_USE); netbufWritePropPtr(dst, prop); netbufWriteU8(dst, usercl->id); netbufWriteS8(dst, tickop); return dst->error; } u32 netmsgSvcPropUseRead(struct netbuf *src, struct netclient *srccl) { struct prop *prop = netbufReadPropPtr(src); const u8 clid = netbufReadU8(src); const s8 tickop = netbufReadS8(src); if (!prop) { return src->error; } struct netclient *actcl = &g_NetClients[clid]; const s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(actcl->playernum); s32 ownop; switch (prop->type) { case PROPTYPE_OBJ: case PROPTYPE_WEAPON: ownop = propobjInteract(prop); break; default: // NOTE: doors are handled with SVC_PROP_DOOR // TODO: eventually remove this message completely ownop = TICKOP_NONE; break; } propExecuteTickOperation(prop, tickop); setCurrentPlayerNum(prevplayernum); return src->error; } u32 netmsgSvcPropDoorWrite(struct netbuf *dst, struct prop *prop, struct netclient *usercl) { if (prop->type != PROPTYPE_DOOR || !prop->door) { return dst->error; } struct doorobj *door = prop->door; netbufWriteU8(dst, SVC_PROP_DOOR); netbufWritePropPtr(dst, prop); netbufWriteU8(dst, usercl ? usercl->id : NET_NULL_CLIENT); netbufWriteS8(dst, door->mode); netbufWriteU32(dst, door->base.flags); netbufWriteU32(dst, door->base.hidden); return dst->error; } u32 netmsgSvcPropDoorRead(struct netbuf *src, struct netclient *srccl) { struct prop *prop = netbufReadPropPtr(src); const u8 clid = netbufReadU8(src); const s8 doormode = netbufReadS8(src); const u32 flags = netbufReadU32(src); const u32 hidden = netbufReadU32(src); struct netclient *actcl = (clid == NET_NULL_CLIENT) ? NULL : &g_NetClients[clid]; if (!prop) { return src->error; } if (!prop->door || prop->type != PROPTYPE_DOOR) { sysLogPrintf(LOG_WARNING, "NET: SVC_PROP_DOOR: prop %u should be a door, but isn't", prop->syncid); return src->error; } const s32 prevplayernum = g_Vars.currentplayernum; if (actcl) { setCurrentPlayerNum(actcl->playernum); } doorSetMode(prop->door, doormode); prop->door->base.hidden = hidden; prop->door->base.flags = flags; if (actcl) { setCurrentPlayerNum(prevplayernum); } return src->error; } u32 netmsgSvcChrDamageWrite(struct netbuf *dst, struct chrdata *chr, f32 damage, struct coord *vector, struct gset *gset, struct prop *aprop, s32 hitpart, bool damageshield, struct prop *prop2, s32 side, s16 *arg11, bool explosion, struct coord *explosionpos) { const u8 flags = damageshield | (explosion << 1) | ((gset != NULL) << 2) | ((aprop != NULL) << 3) | ((prop2 != NULL) << 4) | ((arg11 != NULL) << 5) | ((explosionpos != NULL) << 6); netbufWriteU8(dst, SVC_CHR_DAMAGE); netbufWriteU8(dst, flags); netbufWritePropPtr(dst, chr->prop); netbufWriteF32(dst, damage); netbufWriteCoord(dst, vector); netbufWriteS16(dst, hitpart); netbufWriteS16(dst, side); if (gset) { netbufWriteGset(dst, gset); } if (aprop) { netbufWritePropPtr(dst, aprop); } if (prop2) { netbufWritePropPtr(dst, prop2); } if (arg11) { netbufWriteS16(dst, arg11[0]); netbufWriteS16(dst, arg11[1]); netbufWriteS16(dst, arg11[2]); } if (explosionpos) { netbufWriteCoord(dst, explosionpos); } return dst->error; } u32 netmsgSvcChrDamageRead(struct netbuf *src, struct netclient *srccl) { const u8 flags = netbufReadU8(src); struct prop *chrprop = netbufReadPropPtr(src); const f32 damage = netbufReadF32(src); struct coord vector; netbufReadCoord(src, &vector); const s16 hitpart = netbufReadS16(src); const s16 side = netbufReadS16(src); struct gset gsetvalue; struct gset *gset = NULL; if (flags & (1 << 2)) { netbufReadGset(src, &gsetvalue); gset = &gsetvalue; } struct prop *aprop = (flags & (1 << 3)) ? netbufReadPropPtr(src) : NULL; struct prop *prop2 = (flags & (1 << 4)) ? netbufReadPropPtr(src) : NULL; s16 arg11[3], *arg11ptr = NULL; if (flags & (1 << 5)) { arg11[0] = netbufReadS16(src); arg11[1] = netbufReadS16(src); arg11[2] = netbufReadS16(src); arg11ptr = arg11; } struct coord explosionpos, *explosionposptr = NULL; if (flags & (1 << 6)) { netbufReadCoord(src, &explosionpos); explosionposptr = &explosionpos; } if (src->error) { return src->error; } const bool damageshield = (flags & (1 << 0)) != 0; const bool explosion = (flags & (1 << 1)) != 0; const s32 prevplayernum = g_Vars.currentplayernum; if (chrprop->type == PROPTYPE_PLAYER) { setCurrentPlayerNum(playermgrGetPlayerNumByProp(chrprop)); } chrDamage(chrprop->chr, damage, &vector, gset, aprop, hitpart, damageshield, prop2, NULL, NULL, side, arg11ptr, explosion, explosionposptr); if (chrprop->type == PROPTYPE_PLAYER) { setCurrentPlayerNum(prevplayernum); } return src->error; } u32 netmsgSvcChrDisarmWrite(struct netbuf *dst, struct chrdata *chr, struct prop *aprop, u8 weaponnum, f32 wpndamage, struct coord *wpnpos) { netbufWriteU8(dst, SVC_CHR_DISARM); netbufWritePropPtr(dst, chr->prop); netbufWritePropPtr(dst, aprop); netbufWriteU8(dst, weaponnum); netbufWriteF32(dst, wpndamage); if (wpndamage > 0.f && wpnpos) { netbufWriteCoord(dst, wpnpos); } return dst->error; } u32 netmsgSvcChrDisarmRead(struct netbuf *src, struct netclient *srccl) { struct prop *chrprop = netbufReadPropPtr(src); struct prop *aprop = netbufReadPropPtr(src); const u8 weaponnum = netbufReadU8(src); const f32 weapondmg = netbufReadF32(src); struct coord pos = { 0.f, 0.f, 0.f }; if (src->error) { return src->error; } if (!chrprop || !chrprop->chr) { return 1; } struct chrdata *chr = chrprop->chr; if (chrprop->type == PROPTYPE_CHR) { return src->error; } if (weapondmg > 0.f) { // someone shot a grenade the chr is holding, explode that shit netbufReadCoord(src, &pos); struct weaponobj *weapon = NULL; if (chr->weapons_held[0] && chr->weapons_held[0]->weapon) { weapon = chr->weapons_held[0]->weapon; } else if (chr->weapons_held[1] && chr->weapons_held[1]->weapon) { weapon = chr->weapons_held[1]->weapon; } else { sysLogPrintf(LOG_WARNING, "NET: trying to explode chr %u's gun, but there's no gun", chrprop->syncid); return src->error; } objSetDropped(chrprop, DROPTYPE_DEFAULT); chr->hidden |= CHRHFLAG_DROPPINGITEM; objDamage(&weapon->base, -weapondmg, &pos, weaponnum, g_Vars.currentplayernum); return src->error; } const s32 prevplayernum = g_Vars.currentplayernum; setCurrentPlayerNum(playermgrGetPlayerNumByProp(chrprop)); struct player *player = g_Vars.currentplayer; if (weaponHasFlag(weaponnum, WEAPONFLAG_UNDROPPABLE) || weaponnum > WEAPON_RCP45 || weaponnum <= WEAPON_UNARMED) { setCurrentPlayerNum(prevplayernum); return src->error; } if (weaponnum == WEAPON_RCP120) { player->devicesactive &= ~DEVICE_CLOAKRCP120; } if (weaponnum == WEAPON_CLOAKINGDEVICE) { player->devicesactive &= ~DEVICE_CLOAKDEVICE; } weaponDeleteFromChr(chr, HAND_RIGHT); weaponDeleteFromChr(chr, HAND_LEFT); invRemoveItemByNum(weaponnum); player->hands[1].state = HANDSTATE_IDLE; player->hands[1].ejectstate = EJECTSTATE_INIT; player->hands[1].ejecttype = EJECTTYPE_GUN; player->hands[0].ejectstate = EJECTSTATE_INIT; player->hands[0].ejecttype = EJECTTYPE_GUN; player->hands[0].state = HANDSTATE_IDLE; if (player->visionmode == VISIONMODE_SLAYERROCKET) { struct weaponobj *rocket = g_Vars.currentplayer->slayerrocket; if (rocket && rocket->base.prop) { rocket->timer240 = 0; } player->visionmode = VISIONMODE_NORMAL; } bgunEquipWeapon2(HAND_RIGHT, WEAPON_UNARMED); bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); setCurrentPlayerNum(prevplayernum); return src->error; }