Files
perfect_dark/port/src/net/netmsg.c
T

1348 lines
37 KiB
C

#include <string.h>
#include <stdio.h>
#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;
}