mirror of
https://github.com/perfect-dark-pc-port/perfect_dark.git
synced 2026-10-23 15:52:41 +02:00
1348 lines
37 KiB
C
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;
|
|
}
|