port: net: basic networking with barely working position sync

This commit is contained in:
fgsfds committed 2023-12-17 12:54:48 +01:00
1 parent 0a7faf2409
commit 6fc99e4c23
47 files changed
+7500 -101

No files matched your search

+1 -1
View File
@@ -142,7 +142,7 @@ G_DIR := src/generated/$(ROMID)
ifneq (,$(findstring windows,$(TARGET_PLATFORM)))
TARGET_CFLAGS := $(shell pkg-config sdl2 --cflags-only-I)
TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -static-libstdc++ -ldbghelp
TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -static-libstdc++ -lws2_32 -lwinmm -ldbghelp
# on windows/mingw we need this to be built with a 32-bit compiler so it finds the correct libs
TOOLCHAIN ?= i686-w64-mingw32-
else # assume *nix
+3
View File
@@ -0,0 +1,3 @@
#define ENET_IMPLEMENTATION 1
#define ENET_NO_PRAGMA_LINK 1
#include "external/enet.h"
+2
View File
@@ -0,0 +1,2 @@
#define MINIMP3_IMPLEMENTATION
#include "external/minimp3.h"
+5098
View File
File diff suppressed because it is too large. Load diff
+15
View File
@@ -19,9 +19,24 @@
enum virtkey {
/* same order as SDL scancodes */
VK_KEYBOARD_BEGIN = 0,
VK_A = 4,
VK_Z = 29,
VK_1 = 30,
VK_9 = 38,
VK_0 = 39,
VK_RETURN = 40,
VK_ESCAPE = 41,
VK_BACKSPACE = 42,
VK_SPACE = 44,
VK_MINUS = 45,
VK_LEFTBRACKET = 47,
VK_RIGHTBRACKET = 48,
VK_SEMICOLON = 51,
VK_COMMA = 54,
VK_PERIOD = 55,
VK_DELETE = 76,
VK_LSHIFT = 225,
VK_RSHIFT = 229,
/* same order as SDL mouse buttons */
VK_MOUSE_BEGIN = 512,
+123
View File
@@ -0,0 +1,123 @@
#ifndef _IN_NET_H
#define _IN_NET_H
#include "types.h"
#include "constants.h"
#include "net/netbuf.h"
#define NET_PROTOCOL_VER 1
#define NET_MAX_CLIENTS MAX_PLAYERS
#define NET_MAX_NAME MAX_PLAYERNAME
#define NET_BUFSIZE 1440
#define NET_DEFAULT_PORT 27100
#define NETCHAN_DEFAULT 0
#define NETCHAN_COUNT 1
#define DISCONNECT_UNKNOWN 0
#define DISCONNECT_SHUTDOWN 1
#define DISCONNECT_VERSION 2
#define DISCONNECT_KICKED 3
#define DISCONNECT_BANNED 4
#define DISCONNECT_TIMEOUT 5
#define DISCONNECT_FULL 6
#define CLSTATE_DISCONNECTED 0
#define CLSTATE_CONNECTING 1
#define CLSTATE_AUTH 2
#define CLSTATE_LOBBY 3
#define CLSTATE_GAME 4
#define UCMD_FIRE (1 << 0)
#define UCMD_ACTIVATE (1 << 1)
#define UCMD_RELOAD (1 << 2)
#define UCMD_FIREMODE (1 << 3)
#define UCMD_AIMMODE (1 << 4)
#define UCMD_DUCK (1 << 5)
#define UCMD_SQUAT (1 << 6)
#define UCMD_ZOOMIN (1 << 7)
#define UCMD_SELECT (1 << 8)
#define UCMD_CHAT (1 << 19)
#define UCMD_FL_FORCEPOS (1 << 29)
#define UCMD_FL_FORCEANGLE (1 << 30)
#define UCMD_FL_FORCEGROUND (1 << 31)
#define UCMD_FL_FORCEMASK (UCMD_FL_FORCEPOS | UCMD_FL_FORCEANGLE | UCMD_FL_FORCEGROUND)
struct netplayermove {
u32 tick; // g_NetTIck value when this struct was written; if 0, this struct is invalid
u32 ucmd; // player commands (UCMD_)
f32 leanofs; // analog lean value (-1 .. 1; equal to player->swaytarget / 75.f)
f32 crouchofs; // analog crouch value (-90 for SQUAT, 0 for STAND; player->crouchofs)
f32 movespeed[2]; // move inputs, [0] is forward, [1] is sideways; used mostly for animation
f32 lookspeed[2]; // look inputs, [0] is theta, [1] is verta
f32 angles[2]; // view angles, [0] is theta, [1] is verta
struct coord pos; // player position at g_NetTick == tick
s16 rooms[2]; // cam_room, floorroom
};
struct netclient {
struct _ENetPeer *peer;
u32 id; // remote client number, server is always 0, even on clients
u32 state; // CLSTATE_
u32 flags; // CLFLAG_
struct {
char name[NET_MAX_NAME];
u8 headnum;
u8 bodynum;
} settings;
struct mpplayerconfig *config;
struct player *player;
u8 playernum;
struct netplayermove outmove[2]; // last 2 outgoing player inputs, newest one first
struct netplayermove inmove[2]; // last 2 incoming player inputs, newest one first
u32 inmovetick; // last inmove tick which was applied to the player
u32 outmoveack; // last acked outmove tick
u32 forcetick; // tick on which the client's position was forced, or 0 if not forcing
u32 lerpticks; // how many ticks we've been lerping the position
struct netbuf out; // outbound messages are written here, except broadcasts
struct netbuf in; // incoming packets are fed here
u8 out_data[NET_BUFSIZE]; // buffer for out
};
extern s32 g_NetMode;
// net frame, ticks at 60 fps, starts at 0 when the server is started
extern u32 g_NetTick;
extern u32 g_NetInterpTicks;
extern u32 g_NetExterpTicks;
extern s32 g_NetMaxClients;
extern s32 g_NetNumClients;
extern struct netclient g_NetClients[NET_MAX_CLIENTS + 1]; // last is an extra temporary client
extern struct netclient *g_NetLocalClient;
extern struct netbuf g_NetMsg;
extern struct netbuf g_NetMsgRel;
const char *netFormatClientAddr(const struct netclient *cl);
void netInit(void);
s32 netDisconnect(void);
void netStartFrame(void);
void netEndFrame(void);
s32 netStartServer(u16 port, s32 maxclients);
s32 netStartClient(const char *addr);
u32 netSend(struct netclient *dstcl, struct netbuf *buf, const s32 reliable);
void netServerStageStart(void);
void netServerStageEnd(void);
void netPlayersAllocate(void);
#endif // _IN_NET_H
+47
View File
@@ -0,0 +1,47 @@
#ifndef _IN_NETBUF_H
#define _IN_NETBUF_H
#include "types.h"
struct netbuf {
u8 *data;
u32 size;
u32 rp;
u32 wp;
u32 error;
};
void netbufStartRead(struct netbuf *buf);
void netbufStartReadData(struct netbuf *buf, const void *data, u32 size);
s32 netbufReadLeft(const struct netbuf *buf);
u32 netbufReadData(struct netbuf *buf, void *out, u32 num);
u8 netbufReadU8(struct netbuf *buf);
u16 netbufReadU16(struct netbuf *buf);
u32 netbufReadU32(struct netbuf *buf);
u64 netbufReadU64(struct netbuf *buf);
s8 netbufReadS8(struct netbuf *buf);
s16 netbufReadS16(struct netbuf *buf);
s32 netbufReadS32(struct netbuf *buf);
s64 netbufReadS64(struct netbuf *buf);
f32 netbufReadF32(struct netbuf *buf);
char *netbufReadStr(struct netbuf *buf);
u32 netbufReadCoord(struct netbuf *buf, struct coord *out);
void netbufStartWrite(struct netbuf *buf);
s32 netbufWriteLeft(const struct netbuf *buf);
u32 netbufWriteData(struct netbuf *buf, const void *data, u32 num);
u32 netbufWriteU8(struct netbuf *buf, const u8 v);
u32 netbufWriteU16(struct netbuf *buf, const u16 v);
u32 netbufWriteU32(struct netbuf *buf, const u32 v);
u32 netbufWriteU64(struct netbuf *buf, const u64 v);
u32 netbufWriteS8(struct netbuf *buf, const s8 v);
u32 netbufWriteS16(struct netbuf *buf, const s16 v);
u32 netbufWriteS32(struct netbuf *buf, const s32 v);
u32 netbufWriteS64(struct netbuf *buf, const s64 v);
u32 netbufWriteF32(struct netbuf *buf, const f32 v);
u32 netbufWriteStr(struct netbuf *buf, const char *v);
u32 netbufWriteCoord(struct netbuf *buf, const struct coord *v);
void netbufReset(struct netbuf *buf);
#endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef _IN_NETENET_H
#define _IN_NETENET_H
#define ENET_NO_PRAGMA_LINK 1
#include "external/enet.h"
#undef bool
#undef near
#undef far
#endif
+36
View File
@@ -0,0 +1,36 @@
#ifndef _IN_NETMSG_H
#define _IN_NETMSG_H
#include <PR/ultratypes.h>
#include "net/net.h"
#include "net/netbuf.h"
#define SVC_BAD 0 // trash
#define SVC_NOP 1 // does nothing
#define SVC_AUTH 2 // auth response, sent in response to CLC_AUTH
#define SVC_CHAT 3 // chat message
#define SVC_STAGE_START 4 // start level
#define SVC_STAGE_END 5 // end level
#define SVC_PLAYER_MOVE 6 // player movement
#define CLC_BAD 0 // trash
#define CLC_NOP 1 // does nothing
#define CLC_AUTH 2 // auth request, sent immediately after connecting
#define CLC_CHAT 3 // chat message
#define CLC_MOVE 4 // player input
u32 netmsgClcAuthWrite(struct netbuf *dst);
u32 netmsgClcAuthRead(struct netbuf *src, struct netclient *srccl);
u32 netmsgClcMoveWrite(struct netbuf *dst);
u32 netmsgClcMoveRead(struct netbuf *src, struct netclient *srccl);
u32 netmsgSvcAuthWrite(struct netbuf *dst, struct netclient *authcl);
u32 netmsgSvcAuthRead(struct netbuf *src, struct netclient *srccl);
u32 netmsgSvcStageStartWrite(struct netbuf *dst);
u32 netmsgSvcStageStartRead(struct netbuf *src, struct netclient *srccl);
u32 netmsgSvcStageEndWrite(struct netbuf *dst);
u32 netmsgSvcStageEndRead(struct netbuf *src, struct netclient *srccl);
u32 netmsgSvcPlayerMoveWrite(struct netbuf *dst, struct netclient *movecl);
u32 netmsgSvcPlayerMoveRead(struct netbuf *src, struct netclient *srccl);
#endif
+2
View File
@@ -17,6 +17,7 @@
#include "mod.h"
#include "system.h"
#include "utils.h"
#include "net/net.h"
u32 g_OsMemSize = 0;
s32 g_OsMemSizeMb = 16;
@@ -104,6 +105,7 @@ int main(int argc, const char **argv)
inputInit();
audioInit();
romdataInit();
netInit();
gameInit();
-1
View File
@@ -6,7 +6,6 @@
#include "mixer.h"
#include "platform.h"
#define MINIMP3_IMPLEMENTATION
#include "external/minimp3.h"
#ifdef __SSE4_1__
+622
View File
@@ -0,0 +1,622 @@
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "platform.h"
#include "net/netenet.h"
#include "net/net.h"
#include "net/netbuf.h"
#include "net/netmsg.h"
#include "types.h"
#include "constants.h"
#include "data.h"
#include "bss.h"
#include "game/chraction.h"
#include "game/bot.h"
#include "game/botact.h"
#include "config.h"
#include "system.h"
#include "utils.h"
s32 g_NetMode = NETMODE_NONE;
u32 g_NetTick = 0;
u32 g_NetInterpTicks = 6;
u32 g_NetExterpTicks = 6;
s32 g_NetSimPacketLoss = 20;
s32 g_NetMaxClients = NET_MAX_CLIENTS;
s32 g_NetNumClients = 0;
struct netclient g_NetClients[NET_MAX_CLIENTS + 1]; // last is an extra temporary client
struct netclient *g_NetLocalClient = &g_NetClients[NET_MAX_CLIENTS];
static u8 g_NetMsgBuf[NET_BUFSIZE];
struct netbuf g_NetMsg = { .data = g_NetMsgBuf, .size = sizeof(g_NetMsgBuf) };
static u8 g_NetMsgRelBuf[NET_BUFSIZE];
struct netbuf g_NetMsgRel = { .data = g_NetMsgRelBuf, .size = sizeof(g_NetMsgRelBuf) };
static s32 g_NetInit = false;
static ENetHost *g_NetHost;
static ENetAddress g_NetLocalAddr;
static ENetAddress g_NetRemoteAddr;
static s32 netParseAddr(ENetAddress *out, const char *str)
{
char tmp[256] = { 0 };
if (!str || !str[0]) {
return false;
}
strncpy(tmp, str, sizeof(tmp) - 1);
char *host = tmp;
char *port = NULL;
if (tmp[0] == '[') {
// ipv6 with port: [ADDR]:PORT
host = tmp + 1; // skip [
port = strrchr(host, ']'); // find ]
if (port) {
if (port[1] != ':' || !isdigit(port[2])) {
return false;
}
*port = '\0'; // terminate ip
port += 2; // skip ]:
}
} else if (strchr(host, '.')) {
// ipv4 or hostname
port = strrchr(host, ':');
if (port) {
if (!isdigit(port[1])) {
return false;
}
*port = '\0'; // terminate address
++port; // skip :
}
}
if (!host[0]) {
return false;
}
const s32 portval = port ? atoi(port) : NET_DEFAULT_PORT;
if (portval < 0 || portval > 0xFFFF) {
return false;
}
memset(out, 0, sizeof(*out));
out->port = portval;
if (isdigit(host[0]) || strchr(host, ':')) {
// we stripped off the :PORT at this point and domain names can't start with a digit
// so this must be an IP address
return (enet_address_set_ip(out, host) == 0);
}
// must be a domain name; do a lookup
return (enet_address_set_hostname(out, host) == 0);
}
static const char *netFormatAddr(const ENetAddress *addr)
{
static char str[256];
char tmp[256];
if (addr && enet_address_get_ip(addr, tmp, sizeof(tmp) - 1) == 0) {
if (tmp[0]) {
if (strchr(tmp, ':')) {
// ipv6
snprintf(str, sizeof(str) - 1, "[%s]:%u", tmp, addr->port);
} else {
// ipv4
snprintf(str, sizeof(str) - 1, "%s:%u", tmp, addr->port);
}
return str;
}
}
return NULL;
}
static inline const char *netFormatPeerAddr(const ENetPeer *peer)
{
return netFormatAddr(&peer->address);
}
const char *netFormatClientAddr(const struct netclient *cl)
{
return cl->peer ? netFormatPeerAddr(cl->peer) : "<local>";
}
static inline void netClientReset(struct netclient *cl)
{
memset(cl, 0, sizeof(*cl));
cl->out.data = cl->out_data;
cl->out.size = sizeof(cl->out_data);
cl->id = cl - g_NetClients;
}
static inline void netClientResetAll(void)
{
g_NetMaxClients = NET_MAX_CLIENTS;
g_NetNumClients = 1; // always at least one client, which is us
for (u32 i = 0; i < NET_MAX_CLIENTS + 1; ++i) {
netClientReset(&g_NetClients[i]);
}
}
static inline void netClientRecordMove(struct netclient *cl, const struct player *pl)
{
// make space in the move stack
memmove(cl->outmove + 1, cl->outmove, sizeof(cl->outmove) - sizeof(*cl->outmove));
struct netplayermove *move = &cl->outmove[0];
move->tick = g_NetTick;
move->crouchofs = pl->crouchoffset;
move->leanofs = pl->swaytarget / 75.f;
move->movespeed[0] = pl->speedforwards;
move->movespeed[1] = pl->speedsideways;
move->lookspeed[0] = pl->speedtheta;
move->lookspeed[1] = pl->speedverta;
move->angles[0] = pl->vv_theta;
move->angles[1] = pl->vv_verta;
move->pos = (pl->prop) ? pl->prop->pos : pl->cam_pos;
move->rooms[0] = pl->cam_room;
move->rooms[1] = pl->floorroom;
move->ucmd = pl->ucmd;
if (pl->crouchpos == CROUCHPOS_DUCK) {
move->ucmd |= UCMD_DUCK;
} else if (pl->crouchpos == CROUCHPOS_SQUAT) {
move->ucmd |= UCMD_SQUAT;
}
if (pl->bondactivateorreload & JO_ACTION_ACTIVATE) {
move->ucmd |= UCMD_ACTIVATE;
}
if (pl->bondactivateorreload & JO_ACTION_RELOAD) {
move->ucmd |= UCMD_RELOAD;
}
if (cl != g_NetLocalClient && !cl->forcetick && (move->ucmd & UCMD_FL_FORCEMASK)) {
cl->forcetick = move->tick;
sysLogPrintf(LOG_NOTE, "NET: forcing client %u to move at tick %u", cl->id, cl->forcetick);
}
}
void netInit(void)
{
if (enet_initialize() < 0) {
sysLogPrintf(LOG_ERROR, "NET: could not init ENet, disabling networking");
return;
}
g_NetInit = true;
}
s32 netStartServer(u16 port, s32 maxclients)
{
if (g_NetMode || !g_NetInit) {
return -1;
}
memset(&g_NetLocalAddr, 0, sizeof(g_NetLocalAddr));
g_NetLocalAddr.port = port;
g_NetHost = enet_host_create(&g_NetLocalAddr, maxclients, NETCHAN_COUNT, 0, 0, 0);
if (!g_NetHost) {
sysLogPrintf(LOG_ERROR, "NET: could not create ENet host");
return -2;
}
netClientResetAll();
// the server's local client is client 0
g_NetLocalClient = &g_NetClients[0];
g_NetLocalClient->state = CLSTATE_LOBBY; // local client doesn't need auth
g_NetLocalClient->settings.bodynum = g_PlayerConfigsArray[0].base.mpbodynum;
g_NetLocalClient->settings.headnum = g_PlayerConfigsArray[0].base.mpheadnum;
memcpy(g_NetLocalClient->settings.name, g_PlayerConfigsArray[0].base.name, sizeof(g_NetLocalClient->settings.name));
g_NetMode = NETMODE_SERVER;
g_NetTick = 0;
sysLogPrintf(LOG_NOTE, "NET: created server on port %u", port);
return 0;
}
void netServerStageStart(void)
{
if (g_NetMode != NETMODE_SERVER) {
return;
}
g_NetLocalClient->state = CLSTATE_GAME;
netbufStartWrite(&g_NetMsgRel);
netmsgSvcStageStartWrite(&g_NetMsgRel);
netSend(NULL, &g_NetMsgRel, true);
}
void netServerStageEnd(void)
{
if (g_NetMode != NETMODE_SERVER) {
return;
}
g_NetLocalClient->state = CLSTATE_LOBBY;
netbufStartWrite(&g_NetMsgRel);
netmsgSvcStageEndWrite(&g_NetMsgRel);
netSend(NULL, &g_NetMsgRel, true);
}
s32 netStartClient(const char *addr)
{
if (g_NetMode || !g_NetInit) {
return -1;
}
if (!netParseAddr(&g_NetRemoteAddr, addr)) {
sysLogPrintf(LOG_ERROR, "NET: `%s` is not a valid address", addr);
return -2;
}
memset(&g_NetLocalAddr, 0, sizeof(g_NetLocalAddr));
g_NetHost = enet_host_create(&g_NetLocalAddr, 1, NETCHAN_COUNT, 0, 0, 0);
if (!g_NetHost) {
sysLogPrintf(LOG_ERROR, "NET: could not create ENet host");
return -3;
}
// we'll use the whole array to store what we know of other clients
netClientResetAll();
// for now use last client struct
g_NetLocalClient = &g_NetClients[NET_MAX_CLIENTS];
sysLogPrintf(LOG_NOTE, "NET: connecting to %s...", addr);
g_NetLocalClient->peer = enet_host_connect(g_NetHost, &g_NetRemoteAddr, NETCHAN_COUNT, NET_PROTOCOL_VER);
if (!g_NetLocalClient->peer) {
sysLogPrintf(LOG_WARNING, "NET: could not connect to %s", addr);
enet_host_destroy(g_NetHost);
g_NetHost = NULL;
return -4;
}
g_NetLocalClient->state = CLSTATE_CONNECTING;
g_NetLocalClient->settings.bodynum = g_PlayerConfigsArray[0].base.mpbodynum;
g_NetLocalClient->settings.headnum = g_PlayerConfigsArray[0].base.mpheadnum;
memcpy(g_NetLocalClient->settings.name, g_PlayerConfigsArray[0].base.name, sizeof(g_NetLocalClient->settings.name));
g_NetMode = NETMODE_CLIENT;
g_NetTick = 0;
sysLogPrintf(LOG_NOTE, "NET: waiting for response from %s...", addr);
return 0;
}
s32 netDisconnect(void)
{
if (!g_NetMode) {
return -1;
}
for (s32 i = 0; i < NET_MAX_CLIENTS + 1; ++i) {
if (g_NetClients[i].peer) {
enet_peer_disconnect_now(g_NetClients[i].peer, DISCONNECT_SHUTDOWN);
}
netClientReset(&g_NetClients[i]);
}
g_NetLocalClient = &g_NetClients[NET_MAX_CLIENTS];
// flush pending packets
enet_host_flush(g_NetHost);
// service for a bit just to ensure disconnect gets to peer(s)
enet_host_service(g_NetHost, NULL, 10);
enet_host_destroy(g_NetHost);
g_NetHost = NULL;
g_NetMode = NETMODE_NONE;
sysLogPrintf(LOG_NOTE, "NET: disconnected");
return 0;
}
static void netServerEvConnect(ENetPeer *peer, const u32 data)
{
const char *addrstr = netFormatPeerAddr(peer);
sysLogPrintf(LOG_NOTE, "NET: connection attempt from %s", addrstr);
if (data != NET_PROTOCOL_VER) {
sysLogPrintf(LOG_NOTE, "NET: %s rejected: protocol mismatch", addrstr);
enet_peer_disconnect(peer, DISCONNECT_VERSION);
return;
}
struct netclient *cl = NULL;
// id 0 is the local client
for (s32 i = 1; i < g_NetMaxClients; ++i) {
if (!g_NetClients[i].state) {
cl = &g_NetClients[i];
break;
}
}
if (!cl) {
sysLogPrintf(LOG_NOTE, "NET: %s rejected: server is full", addrstr);
enet_peer_disconnect(peer, DISCONNECT_FULL);
return;
}
netClientReset(cl);
cl->state = CLSTATE_AUTH; // skip CLSTATE_CONNECTING, since we already know it connected
cl->peer = peer;
enet_peer_set_data(peer, cl);
++g_NetNumClients;
}
static void netServerEvDisconnect(struct netclient *cl)
{
sysLogPrintf(LOG_NOTE, "NET: disconnect event from %s", netFormatClientAddr(cl));
if (cl->peer) {
enet_peer_reset(cl->peer);
}
netClientReset(cl);
--g_NetNumClients;
}
static void netServerEvReceive(struct netclient *cl)
{
u32 rc = 0;
u8 msgid = 0;
while (!rc && netbufReadLeft(&cl->in) > 0) {
msgid = netbufReadU8(&cl->in);
switch (msgid) {
case CLC_NOP: rc = 0; break;
case CLC_AUTH: rc = netmsgClcAuthRead(&cl->in, cl); break;
case CLC_CHAT: rc = 0; break;
case CLC_MOVE: rc = netmsgClcMoveRead(&cl->in, cl); break;
default:
rc = 1;
break;
}
}
if (rc) {
sysLogPrintf(LOG_WARNING, "NET: malformed or unknown message 0x%02x from client %s", msgid, netFormatClientAddr(cl));
}
}
static void netClientEvConnect(const u32 data)
{
sysLogPrintf(LOG_NOTE, "NET: connected to server, sending CLC_AUTH");
g_NetLocalClient->state = CLSTATE_AUTH;
// send auth request
netbufStartWrite(&g_NetMsgRel);
netmsgClcAuthWrite(&g_NetMsgRel);
netSend(g_NetLocalClient, &g_NetMsgRel, true);
}
static void netClientEvDisconnect(const u32 reason)
{
sysLogPrintf(LOG_NOTE, "NET: kicked from server, reason: %u", reason);
netDisconnect();
}
static void netClientEvReceive(struct netclient *cl)
{
u32 rc = 0;
u8 msgid = 0;
while (!rc && netbufReadLeft(&cl->in) > 0) {
msgid = netbufReadU8(&cl->in);
switch (msgid) {
case SVC_NOP: rc = 0; break;
case SVC_AUTH: rc = netmsgSvcAuthRead(&cl->in, cl); break;
case SVC_CHAT: rc = 0; break;
case SVC_STAGE_START: rc = netmsgSvcStageStartRead(&cl->in, cl); break;
case SVC_STAGE_END: rc = netmsgSvcStageEndRead(&cl->in, cl); break;
case SVC_PLAYER_MOVE: rc = netmsgSvcPlayerMoveRead(&cl->in, cl); break;
default:
rc = 1;
break;
}
}
if (rc) {
sysLogPrintf(LOG_WARNING, "NET: malformed or unknown message 0x%02x from server", msgid);
}
}
void netStartFrame(void)
{
if (!g_NetMode) {
return;
}
++g_NetTick;
const bool isClient = (g_NetMode == NETMODE_CLIENT);
s32 polled = false;
ENetEvent ev = { .type = ENET_EVENT_TYPE_NONE };
while (!polled) {
if (enet_host_check_events(g_NetHost, &ev) <= 0) {
if (enet_host_service(g_NetHost, &ev, 1) <= 0) {
break;
}
polled = true;
}
switch (ev.type) {
case ENET_EVENT_TYPE_CONNECT:
if (isClient) {
netClientEvConnect(ev.data);
} else if (ev.peer) {
netServerEvConnect(ev.peer, ev.data);
}
break;
case ENET_EVENT_TYPE_DISCONNECT:
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
if (isClient) {
netClientEvDisconnect(ev.type == ENET_EVENT_TYPE_DISCONNECT_TIMEOUT ? DISCONNECT_TIMEOUT : ev.data);
} else if (ev.peer) {
struct netclient *cl = enet_peer_get_data(ev.peer);
if (cl) {
netServerEvDisconnect(cl);
} else {
sysLogPrintf(LOG_WARNING, "NET: disconnect from %s without attached client", netFormatPeerAddr(ev.peer));
}
}
break;
case ENET_EVENT_TYPE_RECEIVE:
if (ev.peer) {
struct netclient *cl = (g_NetMode == NETMODE_CLIENT) ? g_NetLocalClient : enet_peer_get_data(ev.peer);
if (cl && cl->state) {
if (ev.packet->data && ev.packet->dataLength) {
netbufStartReadData(&cl->in, ev.packet->data, ev.packet->dataLength);
if (isClient) {
netClientEvReceive(cl);
} else {
netServerEvReceive(cl);
}
netbufReset(&cl->in);
}
} else if (!isClient) {
sysLogPrintf(LOG_WARNING, "NET: receive from %s without attached client", netFormatPeerAddr(ev.peer));
}
}
enet_packet_dispose(ev.packet);
break;
default:
break;
}
}
netbufStartWrite(&g_NetMsg);
netbufStartWrite(&g_NetMsgRel);
}
void netEndFrame(void)
{
if (!g_NetMode) {
return;
}
if (g_NetLocalClient->state == CLSTATE_GAME && g_NetLocalClient->player && g_NetLocalClient->player->prop) {
if (g_NetMode == NETMODE_CLIENT) {
netClientRecordMove(g_NetLocalClient, g_NetLocalClient->player);
netmsgClcMoveWrite(&g_NetMsg);
} else {
for (s32 i = 0; i < g_NetMaxClients; ++i) {
struct netclient *cl = &g_NetClients[i];
if (cl->state >= CLSTATE_GAME && cl->player) {
netClientRecordMove(cl, cl->player);
netmsgSvcPlayerMoveWrite(cl->forcetick ? &g_NetMsgRel : &g_NetMsg, cl);
}
}
}
}
if (g_NetMsgRel.wp) {
if (g_NetMsgRel.error) {
sysLogPrintf(LOG_WARNING, "NET: reliable out buffer overflow");
}
netSend(NULL, &g_NetMsgRel, true);
}
if (g_NetMsg.wp) {
if (g_NetMsg.error) {
sysLogPrintf(LOG_WARNING, "NET: unreliable out buffer overflow");
}
netSend(NULL, &g_NetMsg, false);
}
enet_host_flush(g_NetHost);
}
u32 netSend(struct netclient *dstcl, struct netbuf *buf, const s32 reliable)
{
if (g_NetMode == NETMODE_CLIENT) {
dstcl = g_NetLocalClient;
}
if (buf == NULL) {
if (dstcl) {
buf = &dstcl->out;
} else {
buf = reliable ? &g_NetMsgRel : &g_NetMsg;
}
}
if (reliable || !g_NetSimPacketLoss || (rand() % g_NetSimPacketLoss) == 0) {
const u32 flags = (reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
ENetPacket *p = enet_packet_create(buf->data, buf->wp, flags);
if (!p) {
sysLogPrintf(LOG_ERROR, "NET: could not alloc %u bytes for packet", buf->wp);
return 0;
}
if (dstcl == NULL) {
enet_host_broadcast(g_NetHost, NETCHAN_DEFAULT, p);
} else {
enet_peer_send(dstcl->peer, NETCHAN_DEFAULT, p);
}
}
const u32 ret = buf->wp;
netbufStartWrite(buf);
return ret;
}
void netPlayersAllocate(void)
{
for (s32 i = 0; i < g_NetMaxClients; ++i) {
struct netclient *cl = &g_NetClients[i];
if (cl->state < CLSTATE_LOBBY) {
continue;
}
if (cl == g_NetLocalClient) {
// we always put the local player at index 0, even client-side
cl->playernum = 0;
} else {
// which means that clientside we have to put the server's player into our slot
cl->playernum = (g_NetMode == NETMODE_CLIENT && i == 0) ? g_NetLocalClient->id : cl->id;
// disable controls for the remote pawns and set their settings
// TODO: backup the player configs or something
struct mpplayerconfig *cfg = &g_PlayerConfigsArray[cl->playernum];
cfg->controlmode = CONTROLMODE_NA;
cfg->base.mpbodynum = cl->settings.bodynum;
cfg->base.mpheadnum = cl->settings.headnum;
memcpy(cfg->base.name, cl->settings.name, MAX_PLAYERNAME);
}
cl->player = g_Vars.players[cl->playernum];
cl->config = &g_PlayerConfigsArray[cl->playernum];
cl->config->client = cl;
cl->player->client = cl;
cl->player->isremote = (cl != g_NetLocalClient);
}
}
+270
View File
@@ -0,0 +1,270 @@
#include <string.h>
#include "types.h"
#include "platform.h"
#include "system.h"
#include "net/netbuf.h"
/* read */
void netbufStartRead(struct netbuf *buf)
{
buf->error = 0;
buf->wp = buf->size;
buf->rp = 0;
}
void netbufStartReadData(struct netbuf *buf, const void *data, u32 size)
{
buf->size = size;
buf->data = (void *)data;
netbufStartRead(buf);
}
s32 netbufReadLeft(const struct netbuf *buf)
{
return buf->wp - buf->rp;
}
static inline u32 netbufCanRead(struct netbuf *buf, const u32 num)
{
if (buf->error || buf->rp + num > buf->wp) {
sysLogPrintf(LOG_ERROR, "NET: could not read %u bytes", num);
__builtin_trap();
buf->error = 1;
return false;
}
return true;
}
u32 netbufReadData(struct netbuf *buf, void *out, u32 num)
{
if (netbufCanRead(buf, num)) {
memcpy(out, buf->data + buf->rp, num);
buf->rp += num;
return num;
}
return 0;
}
u8 netbufReadU8(struct netbuf *buf)
{
if (netbufCanRead(buf, 1)) {
return buf->data[buf->rp++];
}
return 0;
}
u16 netbufReadU16(struct netbuf *buf)
{
if (netbufCanRead(buf, 2)) {
const u16 ret = *(u16 *)&buf->data[buf->rp];
buf->rp += sizeof(ret);
return PD_LE16(ret);
}
return 0;
}
u32 netbufReadU32(struct netbuf *buf)
{
if (netbufCanRead(buf, 4)) {
const u32 ret = *(u32 *)&buf->data[buf->rp];
buf->rp += sizeof(ret);
return PD_LE32(ret);
}
return 0;
}
u64 netbufReadU64(struct netbuf *buf)
{
if (netbufCanRead(buf, 8)) {
const u64 ret = *(u64 *)&buf->data[buf->rp];
buf->rp += sizeof(ret);
return PD_LE64(ret);
}
return 0;
}
s8 netbufReadS8(struct netbuf *buf) {
return (s8)netbufReadU8(buf);
}
s16 netbufReadS16(struct netbuf *buf) {
return (s16)netbufReadU16(buf);
}
s32 netbufReadS32(struct netbuf *buf) {
return (s32)netbufReadU32(buf);
}
s64 netbufReadS64(struct netbuf *buf) {
return (s64)netbufReadU64(buf);
}
f32 netbufReadF32(struct netbuf *buf)
{
union {
f32 f;
u32 i;
} hack;
hack.i = netbufReadU32(buf);
return hack.f;
}
char *netbufReadStr(struct netbuf *buf)
{
const u16 len = netbufReadU16(buf);
if (netbufCanRead(buf, len)) {
char *ret = (char *)&buf->data[buf->rp];
buf->rp += len;
return ret;
}
return NULL;
}
u32 netbufReadCoord(struct netbuf *buf, struct coord *out)
{
if (netbufCanRead(buf, sizeof(*out))) {
out->x = netbufReadF32(buf);
out->y = netbufReadF32(buf);
out->z = netbufReadF32(buf);
return sizeof(*out);
}
return 0;
}
/* write */
void netbufStartWrite(struct netbuf *buf)
{
buf->wp = 0;
buf->error = 0;
}
static inline u32 netbufCanWrite(struct netbuf *buf, const u32 num)
{
if (buf->error || buf->wp + num > buf->size) {
buf->error = 1;
return false;
}
return true;
}
s32 netbufWriteLeft(const struct netbuf *buf)
{
return buf->size - buf->wp;
}
u32 netbufWriteData(struct netbuf *buf, const void *data, u32 num)
{
if (netbufCanWrite(buf, num)) {
memcpy(buf->data + buf->wp, data, num);
buf->wp += num;
return num;
}
return 0;
}
u32 netbufWriteU8(struct netbuf *buf, const u8 v)
{
if (netbufCanWrite(buf, sizeof(v))) {
buf->data[buf->wp++] = v;
return sizeof(v);
}
return 0;
}
u32 netbufWriteU16(struct netbuf *buf, const u16 v)
{
if (netbufCanWrite(buf, sizeof(v))) {
*(u16 *)&buf->data[buf->wp] = PD_LE16(v);
buf->wp += sizeof(v);
return sizeof(v);
}
return 0;
}
u32 netbufWriteU32(struct netbuf *buf, const u32 v)
{
if (netbufCanWrite(buf, sizeof(v))) {
*(u32 *)&buf->data[buf->wp] = PD_LE32(v);
buf->wp += sizeof(v);
return sizeof(v);
}
return 0;
}
u32 netbufWriteU64(struct netbuf *buf, const u64 v)
{
if (netbufCanWrite(buf, sizeof(v))) {
*(u64 *)&buf->data[buf->wp] = PD_LE64(v);
buf->wp += sizeof(v);
return sizeof(v);
}
return 0;
}
u32 netbufWriteS8(struct netbuf *buf, const s8 v)
{
return netbufWriteU8(buf, (u8)v);
}
u32 netbufWriteS16(struct netbuf *buf, const s16 v)
{
return netbufWriteU16(buf, (u16)v);
}
u32 netbufWriteS32(struct netbuf *buf, const s32 v)
{
return netbufWriteU32(buf, (u32)v);
}
u32 netbufWriteS64(struct netbuf *buf, const s64 v)
{
return netbufWriteU64(buf, (u64)v);
}
u32 netbufWriteF32(struct netbuf *buf, const f32 v)
{
const union {
f32 f;
u32 i;
} hack = { .f = v };
return netbufWriteU32(buf, hack.i);
}
u32 netbufWriteStr(struct netbuf *buf, const char *v)
{
if (!v) {
return 0;
}
u32 len = strlen(v) + 1;
if (len > 0xFFFF) {
len = 0xFFFF;
}
if (netbufCanWrite(buf, len + sizeof(u16))) {
netbufWriteU16(buf, len);
netbufWriteData(buf, v, len);
buf->data[buf->wp - 1] = 0;
return sizeof(u16) + len;
}
return 0;
}
u32 netbufWriteCoord(struct netbuf *buf, const struct coord *v)
{
if (netbufCanWrite(buf, sizeof(*v))) {
netbufWriteF32(buf, v->x);
netbufWriteF32(buf, v->y);
netbufWriteF32(buf, v->z);
return sizeof(*v);
}
return 0;
}
void netbufReset(struct netbuf *buf)
{
memset(buf, 0, sizeof(*buf));
}
+362
View File
@@ -0,0 +1,362 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <PR/ultratypes.h>
#include "platform.h"
#include "data.h"
#include "types.h"
#include "game/mainmenu.h"
#include "game/menu.h"
#include "game/gamefile.h"
#include "video.h"
#include "input.h"
#include "config.h"
#include "net/net.h"
extern MenuItemHandlerResult menuhandlerMainMenuCombatSimulator(s32 operation, struct menuitem *item, union handlerdata *data);
static s32 g_NetMenuMaxPlayers = NET_MAX_CLIENTS;
static s32 g_NetMenuPort = NET_DEFAULT_PORT;
static char g_NetJoinAddr[256] = "127.0.0.1:27100";
static s32 g_NetJoinAddrPtr = 0;
/* host */
static MenuItemHandlerResult menuhandlerHostMaxPlayers(s32 operation, struct menuitem *item, union handlerdata *data)
{
switch (operation) {
case MENUOP_GETSLIDER:
data->slider.value = g_NetMenuMaxPlayers;
break;
case MENUOP_SET:
if (data->slider.value) {
g_NetMenuMaxPlayers = data->slider.value;
}
break;
}
return 0;
}
static MenuItemHandlerResult menuhandlerHostStart(s32 operation, struct menuitem *item, union handlerdata *data)
{
if (operation == MENUOP_SET) {
if (netStartServer(g_NetMenuPort, g_NetMenuMaxPlayers) == 0) {
menuhandlerMainMenuCombatSimulator(MENUOP_SET, NULL, NULL);
}
}
return 0;
}
struct menuitem g_NetHostMenuItems[] = {
{
MENUITEMTYPE_SLIDER,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Max Players",
NET_MAX_CLIENTS,
menuhandlerHostMaxPlayers,
},
{
MENUITEMTYPE_SELECTABLE,
0,
0,
L_MPMENU_036, // "Start Game"
0,
menuhandlerHostStart,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
L_OPTIONS_213, // "Back"
0,
NULL,
},
{ MENUITEMTYPE_END },
};
struct menudialogdef g_NetHostMenuDialog = {
MENUDIALOGTYPE_DEFAULT,
(uintptr_t)"Host Network Game",
g_NetHostMenuItems,
NULL,
MENUDIALOGFLAG_LITERAL_TEXT,
NULL,
};
/* join */
static const char *menutextJoinAddress(struct menuitem *item)
{
static char tmp[256 + 1];
if (item && item->flags & MENUITEMFLAG_SELECTABLE_CENTRE) {
// in centered dialog
if (g_NetMode == NETMODE_NONE) {
snprintf(tmp, sizeof(tmp), "%s_\n", g_NetJoinAddr);
} else if (g_NetLocalClient->state == CLSTATE_CONNECTING) {
snprintf(tmp, sizeof(tmp), "Connecting to %s...\n", g_NetJoinAddr);
} else if (g_NetLocalClient->state == CLSTATE_AUTH) {
snprintf(tmp, sizeof(tmp), "Authenticating with %s...\n", g_NetJoinAddr);
} else if (g_NetLocalClient->state == CLSTATE_LOBBY) {
snprintf(tmp, sizeof(tmp), "Waiting for host...\n");
}
} else {
// label
snprintf(tmp, sizeof(tmp), "%s\n", g_NetJoinAddr);
}
return tmp;
}
static MenuItemHandlerResult menuhandlerJoining(s32 operation, struct menuitem *item, union handlerdata *data)
{
if (inputKeyPressed(VK_ESCAPE)) {
netDisconnect();
menuPopDialog();
}
return 0;
}
struct menuitem g_NetJoiningMenuItems[] = {
{
MENUITEMTYPE_LABEL,
0,
MENUITEMFLAG_SELECTABLE_CENTRE,
(uintptr_t)&menutextJoinAddress,
0,
NULL,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"ESC to abort\n",
0,
menuhandlerJoining,
},
{ MENUITEMTYPE_END },
};
struct menudialogdef g_NetJoiningDialog = {
MENUDIALOGTYPE_SUCCESS,
(uintptr_t)"Joining Game...",
g_NetJoiningMenuItems,
NULL,
MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_IGNOREBACK | MENUDIALOGFLAG_STARTSELECTS,
NULL,
};
static MenuItemHandlerResult menuhandlerEnterJoinAddress(s32 operation, struct menuitem *item, union handlerdata *data);
struct menuitem g_NetJoinAddressMenuItems[] = {
{
MENUITEMTYPE_LABEL,
0,
MENUITEMFLAG_SELECTABLE_CENTRE,
(uintptr_t)&menutextJoinAddress,
0,
NULL,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"ESC to return\n",
0,
menuhandlerEnterJoinAddress,
},
{ MENUITEMTYPE_END },
};
struct menudialogdef g_NetJoinAddressDialog = {
MENUDIALOGTYPE_SUCCESS,
(uintptr_t)"Enter Address",
g_NetJoinAddressMenuItems,
NULL,
MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_IGNOREBACK | MENUDIALOGFLAG_STARTSELECTS,
NULL,
};
static MenuItemHandlerResult menuhandlerEnterJoinAddress(s32 operation, struct menuitem *item, union handlerdata *data)
{
if (!menuIsDialogOpen(&g_NetJoinAddressDialog)) {
return 0;
}
const s32 key = inputGetLastKey();
char chr = '\0';
switch (key) {
case 0:
return 0;
case VK_ESCAPE:
menuPopDialog();
break;
case VK_BACKSPACE:
if (g_NetJoinAddrPtr) {
g_NetJoinAddr[--g_NetJoinAddrPtr] = '\0';
} else {
g_NetJoinAddr[0] = '\0';
}
break;
case VK_A ... VK_Z: chr = 'a' + key - VK_A; break;
case VK_1 ... VK_9: chr = '1' + key - VK_1; break;
case VK_0: chr = '0'; break;
case VK_PERIOD: chr = '.'; break;
case VK_SEMICOLON: chr = ':'; break;
case VK_LEFTBRACKET: chr = '['; break;
case VK_RIGHTBRACKET: chr = ']'; break;
case VK_MINUS: chr = '-'; break;
default:
break;
}
if (chr && g_NetJoinAddrPtr < sizeof(g_NetJoinAddr) - 1) {
g_NetJoinAddr[g_NetJoinAddrPtr++] = chr;
g_NetJoinAddr[g_NetJoinAddrPtr + 1] = '\0';
}
inputClearLastKey();
return 0;
}
static MenuItemHandlerResult menuhandlerJoinAddress(s32 operation, struct menuitem *item, union handlerdata *data)
{
if (operation == MENUOP_SET) {
inputClearLastKey();
g_NetJoinAddrPtr = strlen(g_NetJoinAddr);
menuPushDialog(&g_NetJoinAddressDialog);
}
return 0;
}
static MenuItemHandlerResult menuhandlerJoinStart(s32 operation, struct menuitem *item, union handlerdata *data)
{
if (operation == MENUOP_SET) {
if (netStartClient(g_NetJoinAddr) == 0) {
menuPushDialog(&g_NetJoiningDialog);
}
}
return 0;
}
struct menuitem g_NetJoinMenuItems[] = {
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Address: \n",
(uintptr_t)&menutextJoinAddress,
menuhandlerJoinAddress,
},
{
MENUITEMTYPE_SELECTABLE,
0,
0,
L_MPMENU_036, // "Start Game"
0,
menuhandlerJoinStart,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
L_OPTIONS_213, // "Back"
0,
NULL,
},
{ MENUITEMTYPE_END },
};
struct menudialogdef g_NetJoinMenuDialog = {
MENUDIALOGTYPE_DEFAULT,
(uintptr_t)"Join Network Game",
g_NetJoinMenuItems,
NULL,
MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_STARTSELECTS | MENUDIALOGFLAG_IGNOREBACK,
NULL,
};
/* main */
struct menuitem g_NetMenuItems[] = {
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Host Game\n",
0,
(void *)&g_NetHostMenuDialog,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Join Game\n",
0,
(void *)&g_NetJoinMenuDialog,
},
{
MENUITEMTYPE_SEPARATOR,
0,
0,
0,
0,
NULL,
},
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_CLOSESDIALOG,
L_OPTIONS_213, // "Back"
0,
NULL,
},
{ MENUITEMTYPE_END },
};
struct menudialogdef g_NetMenuDialog = {
MENUDIALOGTYPE_DEFAULT,
(uintptr_t)"Network Game",
g_NetMenuItems,
NULL,
MENUDIALOGFLAG_MPLOCKABLE | MENUDIALOGFLAG_LITERAL_TEXT,
NULL,
};
+403
View File
@@ -0,0 +1,403 @@
#include <string.h>
#include "types.h"
#include "data.h"
#include "bss.h"
#include "lib/main.h"
#include "game/mplayer/mplayer.h"
#include "game/chr.h"
#include "game/chraction.h"
#include "system.h"
#include "net/net.h"
#include "net/netbuf.h"
#include "net/netmsg.h"
extern s32 g_StageNum;
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]);
}
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);
}
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->lookspeed[0]);
netbufWriteF32(buf, in->lookspeed[1]);
netbufWriteF32(buf, in->angles[0]);
netbufWriteF32(buf, in->angles[1]);
netbufWriteCoord(buf, &in->pos);
netbufWriteRooms(buf, in->rooms, ARRAYCOUNT(in->rooms));
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->lookspeed[0] = netbufReadF32(buf);
in->lookspeed[1] = netbufReadF32(buf);
in->angles[0] = netbufReadF32(buf);
in->angles[1] = netbufReadF32(buf);
netbufReadCoord(buf, &in->pos);
netbufReadRooms(buf, in->rooms, ARRAYCOUNT(in->rooms));
return buf->error;
}
/* client -> server */
u32 netmsgClcAuthWrite(struct netbuf *dst)
{
netbufWriteU8(dst, CLC_AUTH);
netbufWriteU8(dst, 1); // TODO: number of local players
netbufWriteU8(dst, g_PlayerConfigsArray[0].base.mpbodynum);
netbufWriteU8(dst, g_PlayerConfigsArray[0].base.mpheadnum);
netbufWriteStr(dst, g_PlayerConfigsArray[0].base.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);
return 0;
}
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)
{
if (srccl->state != CLSTATE_GAME) {
sysLogPrintf(LOG_WARNING, "NET: CLC_MOVE from %s, who is not in CLSTATE_GAME", netFormatClientAddr(srccl));
return src->error;
}
struct netplayermove newmove;
srccl->outmoveack = netbufReadU32(src);
netbufReadPlayerMove(src, &newmove);
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 == 0xFF || 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 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);
// 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);
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);
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->player = 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]);
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;
id = netbufReadU8(src);
outmoveack = netbufReadU32(src);
netbufReadPlayerMove(src, &newmove);
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) {
RoomNum newrooms[8] = { newmove.rooms[0], -1 };
chrSetPos(movecl->player->prop->chr, &newmove.pos, newrooms, newmove.angles[0], (newmove.ucmd & UCMD_FL_FORCEGROUND) != 0);
sysLogPrintf(LOG_NOTE, "NET: server is forcing us to move");
}
}
return src->error;
}
+4
View File
@@ -72,6 +72,8 @@
#include "lib/str.h"
#include "data.h"
#include "types.h"
#include "net/net.h"
#include "net/netmsg.h"
extern u8 *g_MempHeap;
extern u32 g_MempHeapSize;
@@ -608,6 +610,8 @@ void mainEndStage(void)
endscreenPrepare();
musicStartMenu();
}
netServerStageEnd();
}
g_MainIsEndscreen = true;
+4
View File
@@ -25,6 +25,7 @@
#include "audio.h"
#include "input.h"
#include "mixer.h"
#include "net/net.h"
/*
* private typedefs and defines
@@ -240,6 +241,7 @@ void schedSubmitTask(OSSched *sc, OSScTask *t)
void schedStartFrame(OSSched *sc)
{
videoStartFrame();
netStartFrame();
}
void schedAudioFrame(OSSched *sc)
@@ -294,6 +296,8 @@ void schedEndFrame(OSSched *sc)
joyReadData();
joy00014238();
netEndFrame();
sndHandleRetrace();
schedAudioFrame(sc);
schedRenderCrashPeriodically(sc->frameCount);
+1
View File
@@ -119,6 +119,7 @@ void sysLogPrintf(s32 level, const char *fmt, ...)
FILE *fout = (level == LOG_NOTE) ? stdout : stderr;
fprintf(fout, "%s%s\n", prefix[level], logmsg);
fflush(fout);
}
void sysFatalError(const char *fmt, ...)
+28 -28
View File
@@ -560,7 +560,7 @@ void amReset(void)
g_AmFont1 = g_CharsHandelGothicSm;
g_AmFont2 = g_FontHandelGothicSm;
#else
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
g_AmFont1 = g_CharsHandelGothicXs;
g_AmFont2 = g_FontHandelGothicXs;
} else {
@@ -591,7 +591,7 @@ s16 amCalculateSlotWidth(void)
}
#if VERSION >= VERSION_NTSC_1_0
if (PLAYERCOUNT() > 1) {
if (LOCALPLAYERCOUNT() > 1) {
max += 3;
} else {
max += 4;
@@ -775,32 +775,32 @@ void amClose(void)
bool amIsCramped(void)
{
#if VERSION == VERSION_JPN_FINAL
if (PLAYERCOUNT() >= 3 && g_AmMenus[g_AmIndex].screenindex != 1) {
if (LOCALPLAYERCOUNT() >= 3 && g_AmMenus[g_AmIndex].screenindex != 1) {
return true;
}
if (IS4MB() && PLAYERCOUNT() == 2) {
if (IS4MB() && LOCALPLAYERCOUNT() == 2) {
return true;
}
if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL
&& PLAYERCOUNT() == 2
&& LOCALPLAYERCOUNT() == 2
&& g_AmMenus[g_AmIndex].screenindex != 1) {
return true;
}
return false;
#else
return (g_AmMenus[g_AmIndex].screenindex == 0 && PLAYERCOUNT() >= 3)
|| (IS4MB() && PLAYERCOUNT() == 2)
|| (PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL);
return (g_AmMenus[g_AmIndex].screenindex == 0 && LOCALPLAYERCOUNT() >= 3)
|| (IS4MB() && LOCALPLAYERCOUNT() == 2)
|| (LOCALPLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL);
#endif
}
void amCalculateSlotPosition(s16 column, s16 row, s16 *x, s16 *y)
{
#if VERSION == VERSION_JPN_FINAL
s32 playercount = PLAYERCOUNT();
s32 playercount = LOCALPLAYERCOUNT();
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
*y = (row - 1) * 50;
@@ -854,7 +854,7 @@ void amCalculateSlotPosition(s16 column, s16 row, s16 *x, s16 *y)
}
}
#elif VERSION >= VERSION_NTSC_1_0
s32 playercount = PLAYERCOUNT();
s32 playercount = LOCALPLAYERCOUNT();
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
*y = (row - 1) * 50;
@@ -906,7 +906,7 @@ void amCalculateSlotPosition(s16 column, s16 row, s16 *x, s16 *y)
}
}
#else
s32 playercount = PLAYERCOUNT();
s32 playercount = LOCALPLAYERCOUNT();
*x = g_AmMenus[g_AmIndex].xradius * (column - 1);
*y = (row - 1) * 50;
@@ -986,15 +986,15 @@ Gfx *amRenderAibotInfo(Gfx *gdl, s32 buddynum)
#endif
#if VERSION >= VERSION_NTSC_1_0
if (PLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
if (LOCALPLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
wide = true;
}
#endif
#if VERSION >= VERSION_NTSC_1_0
if ((PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || PLAYERCOUNT() >= 3)
if ((LOCALPLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || LOCALPLAYERCOUNT() >= 3)
#else
if ((PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || PLAYERCOUNT() >= 3)
if ((LOCALPLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || LOCALPLAYERCOUNT() >= 3)
#endif
{
if ((g_Vars.currentplayernum % 2) == 0) {
@@ -1027,7 +1027,7 @@ Gfx *amRenderAibotInfo(Gfx *gdl, s32 buddynum)
- (s32)(textwidth * 0.5f)
+ offset;
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
y = viGetViewTop() + 5;
} else {
y = viGetViewTop() + 10;
@@ -1043,12 +1043,12 @@ Gfx *amRenderAibotInfo(Gfx *gdl, s32 buddynum)
gdl = func0f1574d0jf(gdl, &x, &y, aibotname, g_AmFont1, g_AmFont2, -1,
0x000000ff, SCREEN_320, SCREEN_240, 0, 0);
y += (PLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
y += (LOCALPLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
#else
gdl = textRender(gdl, &x, &y, aibotname, g_AmFont1, g_AmFont2, -1,
0x000000ff, SCREEN_320, SCREEN_240, 0, 0);
y += (PLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
y += (LOCALPLAYERCOUNT() >= 2) ? 0 : (s32)(textheight * 1.1f);
textMeasure(&textheight, &textwidth, weaponname, g_AmFont1, g_AmFont2, 0);
x = viGetViewLeft() / g_ScaleX
@@ -1077,7 +1077,7 @@ Gfx *amRenderAibotInfo(Gfx *gdl, s32 buddynum)
- (s32)(textwidth * 0.5f)
+ offset;
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
y = viGetViewTop() + 5;
} else {
y = viGetViewTop() + 10;
@@ -1136,7 +1136,7 @@ Gfx *amRenderSlot(Gfx *gdl, char *text, s16 x, s16 y, s32 mode, s32 flags)
paddingtop = 6;
paddingbottom = 6;
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
paddingtop = 5;
paddingbottom = 3;
}
@@ -1275,7 +1275,7 @@ Gfx *amRender(Gfx *gdl)
#endif
#ifndef PLATFORM_N64
if (PLAYERCOUNT() == 1) {
if (LOCALPLAYERCOUNT() == 1) {
gSPSetExtraGeometryModeEXT(gdl++, G_ASPECT_CENTER_EXT);
}
#endif
@@ -1499,7 +1499,7 @@ Gfx *amRender(Gfx *gdl)
above = 6;
below = 6;
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
above = 5;
below = 3;
}
@@ -1522,7 +1522,7 @@ Gfx *amRender(Gfx *gdl)
halfwidth = 1;
above = 2;
below = 0;
} else if (PLAYERCOUNT() >= 2) {
} else if (LOCALPLAYERCOUNT() >= 2) {
s32 textheight;
s32 textwidth;
char text[32];
@@ -1604,16 +1604,16 @@ Gfx *amRender(Gfx *gdl)
}
#endif
barwidth = PLAYERCOUNT() >= 2 ? 48 : 64;
barheight = PLAYERCOUNT() >= 2 ? 7 : 11;
barwidth = LOCALPLAYERCOUNT() >= 2 ? 48 : 64;
barheight = LOCALPLAYERCOUNT() >= 2 ? 7 : 11;
xoffset = 0;
#if VERSION >= VERSION_NTSC_1_0
if ((PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || PLAYERCOUNT() >= 3) {
if ((LOCALPLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) || LOCALPLAYERCOUNT() >= 3) {
xoffset = (g_Vars.currentplayernum & 1) == 0 ? 8 : -8;
}
if (PLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
if (LOCALPLAYERCOUNT() == 1 && optionsGetEffectiveScreenSize() != SCREENSIZE_FULL) {
part1left = viGetViewLeft() / g_ScaleX + 32;
} else {
part1left = (s32) ((viGetViewWidth() / g_ScaleX) * 0.5f)
@@ -1622,7 +1622,7 @@ Gfx *amRender(Gfx *gdl)
+ xoffset;
}
#else
if ((PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || PLAYERCOUNT() >= 3) {
if ((LOCALPLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) || LOCALPLAYERCOUNT() >= 3) {
xoffset = (g_Vars.currentplayernum & 1) == 0 ? 8 : -8;
}
@@ -1648,7 +1648,7 @@ Gfx *amRender(Gfx *gdl)
gDPSetRenderMode(gdl++, G_RM_XLU_SURF, G_RM_XLU_SURF2);
gDPSetCombineMode(gdl++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
y = viGetViewTop() + viGetViewHeight() - (PLAYERCOUNT() >= 2 ? 19 : 34);
y = viGetViewTop() + viGetViewHeight() - (LOCALPLAYERCOUNT() >= 2 ? 19 : 34);
// NTSC beta doesn't scale the health bar when hi-res is on,
// and it only matches if part2left is an inline expression
+1 -1
View File
@@ -1016,7 +1016,7 @@ void eyespyProcessInput(bool allowbuttons)
// Make eyespy look horizontally
if (domovecentre) {
if (g_Vars.currentplayer->eyespy->verta > 0.0f && forwardspeed != 0) {
if (controlmode != CONTROLMODE_PC) {
if (controlmode < CONTROLMODE_PC) {
if (g_Vars.currentplayer->eyespy->verta < 180.0f) {
tmp = g_Vars.currentplayer->eyespy->verta;
+1 -1
View File
@@ -12730,7 +12730,7 @@ Gfx *bgunDrawHud(Gfx *gdl)
{
struct player *player = g_Vars.currentplayer;
s32 bottom = viGetViewTop() + viGetViewHeight() - 13;
s32 playercount = PLAYERCOUNT();
s32 playercount = LOCALPLAYERCOUNT();
s32 playernum = g_Vars.currentplayernum;
struct gunctrl *ctrl;
s32 secs60;
+107 -6
View File
@@ -42,6 +42,8 @@
#include <math.h>
#include "input.h"
#include "video.h"
#include "system.h"
#include "net/net.h"
static void bgunProcessQuickDetonate(struct movedata *data, u32 c1buttons, u32 c1buttonsthisframe, u32 buttons1, u32 buttons2) {
if ((((c1buttons & (buttons1)) && (c1buttonsthisframe & (buttons2)))
@@ -105,6 +107,94 @@ static void bgunProcessInputAltButton(struct movedata *data, s8 contpad, s32 i)
}
}
static inline void bmoveProcessRemoteInput(void)
{
struct player *pl = g_Vars.currentplayer;
struct netplayermove *inmove = &pl->client->inmove[0];
if (inmove->ucmd & UCMD_DUCK) {
pl->crouchpos = CROUCHPOS_DUCK;
} else if (inmove->ucmd & UCMD_SQUAT) {
pl->crouchpos = CROUCHPOS_SQUAT;
} else {
pl->crouchpos = CROUCHPOS_STAND;
}
pl->bondactivateorreload = 0;
if (g_NetMode == NETMODE_SERVER) {
if (inmove->ucmd & UCMD_ACTIVATE) {
pl->bondactivateorreload |= JO_ACTION_ACTIVATE;
}
if (inmove->ucmd & UCMD_RELOAD) {
pl->bondactivateorreload |= JO_ACTION_RELOAD;
}
if (pl->bondactivateorreload) {
bmoveHandleActivate();
}
bgunTickGameplay((inmove->ucmd & UCMD_FIRE) != 0);
if (g_Vars.bondvisible && (bgunIsFiring(HAND_RIGHT) || bgunIsFiring(HAND_LEFT))) {
f32 noiseradius = 0.f;
if (bgunIsFiring(HAND_RIGHT) && bgunGetNoiseRadius(HAND_RIGHT) > noiseradius) {
noiseradius = bgunGetNoiseRadius(HAND_RIGHT);
}
if (bgunIsFiring(HAND_LEFT) && bgunGetNoiseRadius(HAND_LEFT) > noiseradius) {
noiseradius = bgunGetNoiseRadius(HAND_LEFT);
}
chrsCheckForNoise(noiseradius);
}
}
bgunSetSightVisible(GUNSIGHTREASON_NOTAIMING, (inmove->ucmd & UCMD_AIMMODE) != 0);
pl->speedgo = inmove->movespeed[0];
pl->speedforwards = inmove->movespeed[0];
pl->speedstrafe = inmove->movespeed[1];
pl->speedsideways = inmove->movespeed[1];
pl->speedtheta = inmove->lookspeed[0];
pl->speedverta = inmove->lookspeed[1];
pl->vv_theta = inmove->angles[0];
pl->vv_verta = inmove->angles[1];
pl->crouchoffset = inmove->crouchofs;
if (pl->bondmovemode == MOVEMODE_GRAB) {
bgrabUpdateSpeedTheta();
} else if (pl->bondmovemode == MOVEMODE_WALK) {
if (bmoveGetCrouchPos() != CROUCHPOS_STAND) {
pl->speedmaxtime60 = 0;
}
bwalkSetSwayTargetf(inmove->leanofs);
} else if (pl->bondmovemode == MOVEMODE_BIKE) {
// TODO
}
if (inmove->movespeed[0] > 0.95f) {
pl->speedmaxtime60 += g_Vars.lvupdate60;
} else {
pl->speedmaxtime60 = 0;
}
if (pl->speedforwards > 1) {
pl->speedforwards = 1;
}
if (pl->speedforwards < -1) {
pl->speedforwards = -1;
}
if (pl->speedsideways > 1) {
pl->speedsideways = 1;
}
if (pl->speedsideways < -1) {
pl->speedsideways = -1;
}
pl->speedforwards *= 1.08f;
pl->speedforwards *= pl->speedboost;
}
#endif // PLATFORM_N64
void bmoveSetControlDef(u32 controldef)
@@ -313,6 +403,12 @@ void bmoveApplyMoveData(struct movedata *data)
} else if (g_Vars.currentplayer->bondmovemode == MOVEMODE_WALK) {
bwalkApplyMoveData(data);
}
#ifndef PLATFORM_N64
// record some inputs if this is a local player
if (g_NetMode && !g_Vars.currentplayer->isremote) {
g_Vars.currentplayer->ucmd = 0;
}
#endif
}
void bmoveUpdateSpeedTheta(void)
@@ -715,6 +811,11 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
const f32 mlookscale = g_Vars.lvupdate240 ? (4.f / (f32)g_Vars.lvupdate240) : 4.f;
const bool allowmlook = (g_Vars.currentplayernum == 0) && (allowc1x || allowc1y);
bool allowmcross = false;
if (g_Vars.currentplayer->isremote) {
bmoveProcessRemoteInput();
return;
}
#endif
controlmode = optionsGetControlMode(g_Vars.currentplayerstats->mpindex);
@@ -1025,7 +1126,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
}
// Handle B button activation
if (allowc1buttons && controlmode != CONTROLMODE_PC) {
if (allowc1buttons && controlmode < CONTROLMODE_PC) {
for (i = 0; i < numsamples; i++) {
if (joyGetButtonsOnSample(i, contpad1, c1allowedbuttons & B_BUTTON)
|| joyGetButtonsOnSample(i, contpad2, c2allowedbuttons & B_BUTTON)) {
@@ -1493,7 +1594,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
for (i = 0; i < numsamples; i++) {
if (joyGetButtonsOnSample(i, contpad1, c1allowedbuttons & usemask)) {
if (g_Vars.currentplayer->usedowntime >= -1) {
if (controlmode != CONTROLMODE_PC) {
if (controlmode < CONTROLMODE_PC) {
if (joyGetButtonsPressedOnSample(i, contpad1, shootbuttons & c1allowedbuttons)
&& g_Vars.currentplayer->usedowntime >= 0
&& bgunConsiderToggleGunFunction(g_Vars.currentplayer->usedowntime, true, false, 0) != USETIMER_CONTINUE) {
@@ -1518,7 +1619,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
}
}
} else {
if ((controlmode != CONTROLMODE_PC) && g_Vars.currentplayer->usedowntime >= -2) {
if ((controlmode < CONTROLMODE_PC) && g_Vars.currentplayer->usedowntime >= -2) {
bgunConsiderToggleGunFunction(g_Vars.currentplayer->usedowntime, false, false, 0);
}
}
@@ -1664,7 +1765,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
#ifdef PLATFORM_N64
if (allowc1buttons) {
#else
if (allowc1buttons && (controlmode != CONTROLMODE_PC || (PLAYER_EXTCFG().crouchmode & CROUCHMODE_ANALOG))) {
if (allowc1buttons && (controlmode < CONTROLMODE_PC || (PLAYER_EXTCFG().crouchmode & CROUCHMODE_ANALOG))) {
#endif
for (i = 0; i < numsamples; i++) {
if (!canmanualzoom && aimonhist[i]) {
@@ -1748,7 +1849,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
}
// Handle mine detonation
if (controlmode != CONTROLMODE_PC) {
if (controlmode < CONTROLMODE_PC) {
if ((((c1buttons & invbuttons) && (c1buttonsthisframe & B_BUTTON))
|| ((c1buttons & B_BUTTON) && (c1buttonsthisframe & invbuttons)))
&& weaponnum == WEAPON_REMOTEMINE) {
@@ -1781,7 +1882,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
movedata.triggeron = g_Vars.currentplayer->waitforzrelease == false
&& (c1buttons & shootbuttons)
&& g_Vars.currentplayer->pausemode == PAUSEMODE_UNPAUSED;
if (controlmode != CONTROLMODE_PC) {
if (controlmode < CONTROLMODE_PC) {
movedata.triggeron = movedata.triggeron && ((c1buttons & invbuttons) == 0);
}
}
+68 -1
View File
@@ -27,7 +27,67 @@
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
extern f32 fabsf(f32);
#include "net/net.h"
#include "system.h"
static void bwalkUpdateRemote(void)
{
struct player *pl = g_Vars.currentplayer;
const struct netplayermove *inmove = &pl->client->inmove[0];
const struct netplayermove *inmoveprev = &pl->client->inmove[1];
struct coord delta, target;
f32 spc8, spc4;
s32 moveticks = inmove->tick - inmoveprev->tick;
if (moveticks > g_NetInterpTicks) {
moveticks = g_NetInterpTicks;
}
const bool forcepos = !inmoveprev->tick || !moveticks ||
(inmove->ucmd & UCMD_FL_FORCEPOS) ||
(fabsf(pl->prop->pos.x - inmove->pos.x) > 256.f) ||
(fabsf(pl->prop->pos.y - inmove->pos.y) > 256.f) ||
(fabsf(pl->prop->pos.z - inmove->pos.z) > 256.f);
pl->client->inmovetick = inmove->tick;
pl->bondshotspeed.x = 0.f;
pl->bondshotspeed.y = 0.f;
pl->bondshotspeed.z = 0.f;
pl->bondbreathing = 0.f;
if (forcepos) {
// no interpolation, set position and lock the lad in place
delta.x = 0.f;
delta.y = 0.f;
delta.z = 0.f;
pl->prop->pos = inmove->pos;
pl->client->lerpticks = g_NetInterpTicks + g_NetExterpTicks;
} else if (pl->client->lerpticks <= moveticks) {
// lerp towards the new position
const f32 t = (f32)pl->client->lerpticks / (f32)moveticks;
target.x = inmoveprev->pos.x + (inmove->pos.x - inmoveprev->pos.x) * t;
target.y = inmoveprev->pos.y + (inmove->pos.y - inmoveprev->pos.y) * t;
target.z = inmoveprev->pos.z + (inmove->pos.z - inmoveprev->pos.z) * t;
delta.x = target.x - pl->prop->pos.x;
delta.y = target.y - pl->prop->pos.y;
delta.z = target.z - pl->prop->pos.z;
pl->client->lerpticks += g_Vars.lvupdate60;
} else if (pl->client->lerpticks <= moveticks + g_NetExterpTicks) {
// try extrapolating based on inputs for a while
pl->client->lerpticks += g_Vars.lvupdate60;
bwalk0f0c69b8();
return;
} else {
// we're too far gone, lock position
delta.x = 0.f;
delta.y = 0.f;
delta.z = 0.f;
pl->prop->pos = inmove->pos;
}
bwalkUpdateCrouchOffset();
bwalk0f0c63bc(&delta, g_Vars.currentplayer->swaytarget == 0.0f, CDTYPE_ALL);
}
#endif
void bwalkInit(void)
@@ -1767,7 +1827,14 @@ void bwalkTick(void)
bwalkUpdatePrevPos();
bwalkUpdateTheta();
bmoveUpdateVerta();
#ifndef PLATFORM_N64
if (g_Vars.currentplayer->isremote) {
bwalkUpdateRemote();
} else
#endif
bwalk0f0c69b8();
bwalkUpdateVertical();
#if VERSION >= VERSION_NTSC_1_0
+79
View File
@@ -56,6 +56,10 @@
#include "lib/vi.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#include "net/netmsg.h"
#endif
s32 g_RecentQuipsPlayed[5];
u32 var8009cd84;
@@ -4272,6 +4276,13 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse
s32 aplayernum = -1;
s32 choketype = CHOKETYPE_NONE;
#ifndef PLATFORM_N64
if (g_NetMode == NETMODE_CLIENT) {
// don't do anything if we're not the authority
return;
}
#endif
if (hitpart == HITPART_HEAD) {
choketype = CHOKETYPE_GURGLE;
}
@@ -15287,6 +15298,68 @@ void func0f04b740(void)
// empty
}
bool chrSetPos(struct chrdata *chr, struct coord *pos, RoomNum *rooms, f32 theta, bool findground)
{
const f32 angle = BADDEG2RAD(360.f - theta);
if (findground) {
const u32 oldhidden = chr->hidden;
chr->hidden |= CHRHFLAG_WARPONSCREEN;
const bool ret = chrMoveToPos(chr, pos, rooms, angle, true);
if ((oldhidden & CHRHFLAG_WARPONSCREEN) == 0) {
chr->hidden &= ~CHRHFLAG_WARPONSCREEN;
}
}
bool newrooms = false;
for (s32 i = 0; i < ARRAYCOUNT(chr->prop->rooms) && rooms[i] >= 0; ++i) {
if (chr->prop->rooms[i] != rooms[i]) {
newrooms = true;
break;
}
}
propSetPerimEnabled(chr->prop, false);
chr->prop->pos = *pos;
const f32 ground = cdFindGroundInfoAtCyl(pos, chr->radius, rooms, &chr->floorcol,
&chr->floortype, NULL, &chr->floorroom, NULL, NULL);
chr->ground = ground;
chr->manground = ground;
chr->sumground = ground * (PAL ? 8.4175090789795f : 9.999998f);
if (newrooms) {
propDeregisterRooms(chr->prop);
roomsCopy(rooms, chr->prop->rooms);
chr0f0220ac(chr);
}
modelSetRootPosition(chr->model, pos);
const u16 nodetype = chr->model->definition->rootnode->type;
if ((nodetype & 0xff) == MODELNODETYPE_CHRINFO) {
union modelrwdata *rwdata = modelGetNodeRwData(chr->model, chr->model->definition->rootnode);
rwdata->chrinfo.ground = ground;
}
chrSetLookAngle(chr, angle);
if (chr->prop->type == PROPTYPE_PLAYER) {
struct player *player = g_Vars.players[playermgrGetPlayerNumByProp(chr->prop)];
player->vv_manground = ground;
player->vv_ground = ground;
player->vv_theta = theta;
player->unk1c64 = 1;
}
propSetPerimEnabled(chr->prop, true);
return true;
}
bool chrMoveToPos(struct chrdata *chr, struct coord *pos, RoomNum *rooms, f32 angle, bool force)
{
struct coord pos2;
@@ -15344,6 +15417,12 @@ bool chrMoveToPos(struct chrdata *chr, struct coord *pos, RoomNum *rooms, f32 an
player->unk1c64 = 1;
}
#ifndef PLATFORM_N64
if (g_NetMode == NETMODE_SERVER && player->isremote) {
player->ucmd |= UCMD_FL_FORCEPOS | UCMD_FL_FORCEANGLE | UCMD_FL_FORCEGROUND;
}
#endif
result = true;
}
+16 -16
View File
@@ -86,7 +86,7 @@ u8 hudmsgsAreActive(void)
s32 hudmsgIsZoomRangeVisible(void)
{
return optionsGetShowZoomRange(g_Vars.currentplayerstats->mpindex)
&& (PLAYERCOUNT() == 1
&& (LOCALPLAYERCOUNT() == 1
|| !g_Vars.mplayerisrunning
|| g_Vars.coopplayernum >= 0
|| g_Vars.antiplayernum >= 0)
@@ -131,7 +131,7 @@ Gfx *hudmsgRenderMissionTimer(Gfx *gdl, u32 alpha)
viewleft = viGetViewLeft() / g_ScaleX;
viewtop = viGetViewTop();
viewheight = viGetViewHeight();
playercount = PLAYERCOUNT();
playercount = LOCALPLAYERCOUNT();
playernum = g_Vars.currentplayernum;
timery = viewheight;
@@ -190,7 +190,7 @@ Gfx *hudmsgRenderMissionTimer(Gfx *gdl, u32 alpha)
y = timery;
#ifndef PLATFORM_N64
if (PLAYERCOUNT() < 2) {
if (LOCALPLAYERCOUNT() < 2) {
gSPExtraGeometryModeEXT(gdl++, G_ASPECT_MODE_EXT, g_HudAlignModeL);
}
#endif
@@ -234,7 +234,7 @@ Gfx *hudmsgRenderZoomRange(Gfx *gdl, u32 alpha)
texty = viewheight + viewtop - 1;
maxzoom = 1.0f;
weaponnum = g_Vars.currentplayer->hands[0].gset.weaponnum;
playercount = PLAYERCOUNT();
playercount = LOCALPLAYERCOUNT();
texty -= 17;
@@ -374,7 +374,7 @@ s32 hudmsg0f0ddb1c(s32 *arg0, s32 arg1)
*arg0 = 24;
if (PLAYERCOUNT() == 2
if (LOCALPLAYERCOUNT() == 2
&& optionsGetScreenSplit() == SCREENSPLIT_VERTICAL
&& (!g_InCutscene || g_MainIsEndscreen)) {
result -= *arg0 * 2 / 3;
@@ -388,7 +388,7 @@ s32 hudmsg0f0ddb1c(s32 *arg0, s32 arg1)
result = result + viewwidth - *arg0 - arg1 - 11;
if (PLAYERCOUNT() == 1 || (PLAYERCOUNT() == 2 && g_InCutscene && !g_MainIsEndscreen)) {
if (LOCALPLAYERCOUNT() == 1 || (LOCALPLAYERCOUNT() == 2 && g_InCutscene && !g_MainIsEndscreen)) {
result -= 16;
#if VERSION < VERSION_JPN_FINAL
@@ -403,7 +403,7 @@ s32 hudmsg0f0ddb1c(s32 *arg0, s32 arg1)
*arg0 = 24;
if (PLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) {
if (LOCALPLAYERCOUNT() == 2 && optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) {
result -= *arg0 * 2 / 3;
if (g_Vars.currentplayernum == 0) {
@@ -415,7 +415,7 @@ s32 hudmsg0f0ddb1c(s32 *arg0, s32 arg1)
result = result + viewwidth - *arg0 - arg1 - 11;
if (PLAYERCOUNT() == 1) {
if (LOCALPLAYERCOUNT() == 1) {
result -= 16;
}
#endif
@@ -857,7 +857,7 @@ void hudmsgCalculatePosition(struct hudmessage *msg)
#if VERSION >= VERSION_NTSC_1_0
s32 offset = (msg->alignh == HUDMSGALIGN_XMIDDLE) ? 10 : 0;
if (PLAYERCOUNT() >= 3) {
if (LOCALPLAYERCOUNT() >= 3) {
viewwidth -= offset;
if (g_Vars.currentplayernum == 0 || g_Vars.currentplayernum == 2) {
@@ -865,7 +865,7 @@ void hudmsgCalculatePosition(struct hudmessage *msg)
}
}
if (PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) {
if (LOCALPLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())) {
#if VERSION >= VERSION_PAL_FINAL
if (!g_InCutscene || g_MainIsEndscreen)
#endif
@@ -888,7 +888,7 @@ void hudmsgCalculatePosition(struct hudmessage *msg)
x = viewleft + v0 + msg->xmargin + 3;
if (PLAYERCOUNT() == 2
if (LOCALPLAYERCOUNT() == 2
&& (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || IS4MB())
&& (!g_InCutscene || g_MainIsEndscreen)) {
if (IS4MB()) {
@@ -904,7 +904,7 @@ void hudmsgCalculatePosition(struct hudmessage *msg)
x += 4;
}
}
} else if (PLAYERCOUNT() >= 3) {
} else if (LOCALPLAYERCOUNT() >= 3) {
if ((msg->playernum % 2) == 0) {
x--;
} else {
@@ -933,13 +933,13 @@ void hudmsgCalculatePosition(struct hudmessage *msg)
case HUDMSGALIGN_BOTTOM:
y = viewtop + viewheight - msg->height - msg->ymargin - 14;
if (PLAYERCOUNT() == 2 && (g_InCutscene == 0 || g_MainIsEndscreen)) {
if (LOCALPLAYERCOUNT() == 2 && (g_InCutscene == 0 || g_MainIsEndscreen)) {
if (IS4MB() || (optionsGetScreenSplit() != SCREENSPLIT_VERTICAL && msg->playernum == 0)) {
y += 8;
} else {
y += 3;
}
} else if (PLAYERCOUNT() >= 3) {
} else if (LOCALPLAYERCOUNT() >= 3) {
if (msg->playernum <= 1) {
y += 8;
} else {
@@ -1284,7 +1284,7 @@ void hudmsgsTick(void)
&& msg->type != HUDMSGTYPE_CUTSCENESUBTITLE
&& msg->type != HUDMSGTYPE_INGAMESUBTITLE
#endif
&& PLAYERCOUNT() == 1) {
&& LOCALPLAYERCOUNT() == 1) {
#if VERSION >= VERSION_NTSC_1_0
sndStart(var80095200, SFX_HUDMSG, NULL, -1, -1, -1, -1, -1);
#else
@@ -1383,7 +1383,7 @@ Gfx *hudmsgsRender(Gfx *gdl)
s32 timerthing = 255;
s32 spdc = true;
#ifndef PLATFORM_N64
const s32 playercount = PLAYERCOUNT();
const s32 playercount = LOCALPLAYERCOUNT();
#endif
#if PAL
+15
View File
@@ -96,6 +96,10 @@
#include "lib/vars.h"
#include "lib/vi.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#include "net/netmsg.h"
#endif
struct sndstate *g_MiscSfxAudioHandles[3];
u32 var800aa5bc;
@@ -360,6 +364,11 @@ void lvReset(s32 stagenum)
g_Vars.playerstats[i].kills[j] = 0;
}
}
#ifndef PLATFORM_N64
// if we're a server, signal to clients that the level is changing
netServerStageStart();
#endif
}
mpSetDefaultNamesIfEmpty();
@@ -1083,6 +1092,12 @@ Gfx *lvRender(Gfx *gdl)
struct player *player;
struct chrdata *chr;
#ifndef PLATFORM_N64
if (g_NetMode) {
forcesingleplayer = true;
}
#endif
playercount = forcesingleplayer ? 1 : PLAYERCOUNT();
gSPClipRatio(gdl++, FRUSTRATIO_2);
+23 -2
View File
@@ -33,6 +33,9 @@
#include "lib/str.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
u8 g_InventoryWeapon;
@@ -41,6 +44,7 @@ struct menudialogdef g_CiControlPlayer2MenuDialog;
struct menudialogdef g_CinemaMenuDialog;
#ifndef PLATFORM_N64
extern struct menudialogdef g_ExtendedMenuDialog;
extern struct menudialogdef g_NetMenuDialog;
#endif
char *menuTextCurrentStageName(struct menuitem *item)
@@ -4865,6 +4869,11 @@ MenuDialogHandlerResult menudialogMainMenu(s32 operation, struct menudialogdef *
switch (operation) {
case MENUOP_OPEN:
g_Menus[g_MpPlayerNum].main.unke2c = 0;
#ifndef PLATFORM_N64
if (g_NetMode) {
netDisconnect();
}
#endif
break;
case MENUOP_TICK:
if (g_Menus[g_MpPlayerNum].curdialog &&
@@ -4942,24 +4951,36 @@ struct menuitem g_MainMenuMenuItems[] = {
0x00000005,
menuhandlerMainMenuCounterOperative,
},
#ifndef PLATFORM_N64
{
MENUITEMTYPE_SELECTABLE,
4,
MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Network Game",
0x00000006,
(void *)&g_NetMenuDialog,
},
#endif
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT,
L_OPTIONS_187, // "Change Agent..."
0x00000006,
0x00000007,
(void *)&g_ChangeAgentMenuDialog,
},
/*
#ifndef PLATFORM_N64
{
MENUITEMTYPE_SELECTABLE,
0,
MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT | MENUITEMFLAG_LITERAL_TEXT,
(uintptr_t)"Exit Game",
0x00000007,
0x00000008,
(void *)&g_ExitGameMenuDialog,
},
#endif
*/
{ MENUITEMTYPE_END },
};
+14
View File
@@ -50,6 +50,7 @@
#include "types.h"
#ifndef PLATFORM_N64
#include "video.h"
#include "net/net.h"
#define BLUR_OFS 10
#else
#define BLUR_OFS 30
@@ -5375,6 +5376,19 @@ Gfx *menuRender(Gfx *gdl)
gdl = text0f153628(gdl);
#ifndef PLATFORM_N64
if (g_NetMode) {
if (g_NetMode == NETMODE_SERVER) {
sprintf(text, "Server: %d/%d", g_NetNumClients, g_NetMaxClients);
} else {
sprintf(text, "Client: ID %u", g_NetLocalClient->id);
}
x = viewleft + 2;
y = viewtop + 2;
colour = 0x5070ffff;
gdl = textRenderProjected(gdl, &x, &y, text, g_CharsHandelGothicSm, g_FontHandelGothicSm, colour, viGetWidth(), viGetHeight(), 0, 0);
} else
#endif
for (i = 0; i < MAX_PLAYERS; i++) {
// Figure out what text will be displayed. The text calculated
// here is for measuring purposes only and isn't rendered.
+2 -2
View File
@@ -4117,7 +4117,7 @@ Gfx *menuitemControllerRenderText(Gfx *gdl, s32 curmode, struct menurendercontex
// during development the second player in the 2.x styles had to
// choose their control style separately to player 1, in which case
// there would have been 2.5, 2.6, 2.7 and 2.8 for player 2.
if (curmode > CONTROLMODE_24 && curmode != CONTROLMODE_PC) {
if (curmode > CONTROLMODE_24 && curmode < CONTROLMODE_PC) {
if (textnum == menuitemControllerGetButtonAction(prevmode + 4, i)) {
colour = labelcolour;
}
@@ -4202,7 +4202,7 @@ Gfx *menuitemControllerRender(Gfx *gdl, struct menurendercontext *context)
data->prevmode = -1;
}
} else {
if (g_Menus[g_MpPlayerNum].main.controlmode >= CONTROLMODE_21 && g_Menus[g_MpPlayerNum].main.controlmode != CONTROLMODE_PC) {
if (g_Menus[g_MpPlayerNum].main.controlmode >= CONTROLMODE_21 && g_Menus[g_MpPlayerNum].main.controlmode < CONTROLMODE_PC) {
data->controlgroup = 1;
data->contfadetimer = 0;
data->prevmode = -1;
+9
View File
@@ -27,6 +27,9 @@
#include "lib/snd.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
u8 g_FileState = 0;
u8 var80062944 = 0;
@@ -355,6 +358,12 @@ void menuTick(void)
canjoin = true;
}
#ifndef PLATFORM_N64
if (g_NetMode) {
canjoin = false;
}
#endif
if (g_BossFile.locktype == MPLOCKTYPE_CHALLENGE) {
g_PlayerConfigsArray[i].base.team = 0;
}
+24 -6
View File
@@ -28,6 +28,9 @@
#include "lib/lib_317f0.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
// bss
struct chrdata *g_MpAllChrPtrs[MAX_MPCHRS];
@@ -176,14 +179,29 @@ void mpStartMatch(void)
s32 numplayers = 0;
s32 stagenum;
mpConfigureQuickTeamSimulants();
if (!challengeIsFeatureUnlocked(MPFEATURE_ONEHITKILLS)) {
g_MpSetup.options &= ~MPOPTION_ONEHITKILLS;
#ifndef PLATFORM_N64
if (g_NetMode == NETMODE_SERVER) {
g_MpSetup.chrslots &= ~0x000f;
g_MpSetup.chrslots |= 1;
for (s32 i = 1; i < g_NetMaxClients; ++i) {
if (g_NetClients[i].state >= CLSTATE_LOBBY) {
g_MpSetup.chrslots |= (1 << i);
}
}
}
if (!challengeIsFeatureUnlocked(MPFEATURE_SLOWMOTION)) {
g_MpSetup.options &= ~(MPOPTION_SLOWMOTION_ON | MPOPTION_SLOWMOTION_SMART);
if (g_NetMode != NETMODE_CLIENT)
#endif
{
mpConfigureQuickTeamSimulants();
if (!challengeIsFeatureUnlocked(MPFEATURE_ONEHITKILLS)) {
g_MpSetup.options &= ~MPOPTION_ONEHITKILLS;
}
if (!challengeIsFeatureUnlocked(MPFEATURE_SLOWMOTION)) {
g_MpSetup.options &= ~(MPOPTION_SLOWMOTION_ON | MPOPTION_SLOWMOTION_SMART);
}
}
for (i = 0; i < MAX_PLAYERS; i++) {
+1 -1
View File
@@ -1037,7 +1037,7 @@ struct prop *scenarioCreateObj(s32 modelnum, s16 padnum, f32 arg2, u32 flags, u3
*/
void scenarioCreateHudmsg(s32 playernum, char *message)
{
if (playernum >= 0 && playernum < PLAYERCOUNT()) {
if (playernum >= 0 && playernum < LOCALPLAYERCOUNT()) {
s32 prevplayernum = g_Vars.currentplayernum;
setCurrentPlayerNum(playernum);
+1 -1
View File
@@ -343,7 +343,7 @@ bool musicIsAnyPlayerInAmbientRoom(void)
return false;
}
for (i = 0; i < PLAYERCOUNT(); i++) {
for (i = 0; i < LOCALPLAYERCOUNT(); i++) {
if (g_Vars.players[i]->prop
&& g_Vars.players[i]->prop->rooms
&& g_Vars.players[i]->prop->rooms[0] != -1) {
+1 -1
View File
@@ -319,7 +319,7 @@ Gfx *nbombCreateGdl(void)
Gfx *gdl;
s32 index = 0;
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
index = 1;
}
+15 -9
View File
@@ -2815,6 +2815,12 @@ s16 playerGetFbHeight(void)
#if VERSION >= VERSION_NTSC_1_0
bool playerHasSharedViewport(void)
{
#ifndef PLATFORM_N64
if (g_NetMode) {
return true;
}
#endif
if ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0)
&& menuGetRoot() == MENUROOT_MPENDSCREEN
&& var8009dfc0 == 0) {
@@ -2835,14 +2841,14 @@ s16 playerGetViewportWidth(void)
if ((!g_InCutscene || g_MainIsEndscreen) && menuGetRoot() != MENUROOT_COOPCONTINUE)
#endif
{
if (PLAYERCOUNT() >= 3) {
if (LOCALPLAYERCOUNT() >= 3) {
// 3/4 players
width = g_ViModes[g_ViRes].width / 2;
if (g_Vars.currentplayernum == 0 || g_Vars.currentplayernum == 2) {
width--;
}
} else if (PLAYERCOUNT() == 2) {
} else if (LOCALPLAYERCOUNT() == 2) {
if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || g_Vars.fourmeg2player) {
// 2 players vsplit
width = g_ViModes[g_ViRes].width / 2;
@@ -2875,7 +2881,7 @@ s16 playerGetViewportLeft(void)
#endif
s16 left;
if (PLAYERCOUNT() >= 3 && something != 0) {
if (LOCALPLAYERCOUNT() >= 3 && something != 0) {
if (g_Vars.currentplayernum == 1 || g_Vars.currentplayernum == 3) {
// 3/4 players - right side
left = g_ViModes[g_ViRes].width / 2 + g_ViModes[g_ViRes].fbwidth - g_ViModes[g_ViRes].width;
@@ -2883,7 +2889,7 @@ s16 playerGetViewportLeft(void)
// 3/4 players - left side
left = g_ViModes[g_ViRes].fbwidth - g_ViModes[g_ViRes].width;
}
} else if (PLAYERCOUNT() == 2 && something != 0) {
} else if (LOCALPLAYERCOUNT() == 2 && something != 0) {
if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || g_Vars.fourmeg2player) {
if (g_Vars.currentplayernum == 1) {
// 2 players vsplit - right side
@@ -2908,7 +2914,7 @@ s16 playerGetViewportHeight(void)
{
s16 height;
if (PLAYERCOUNT() >= 2
if (LOCALPLAYERCOUNT() >= 2
#if VERSION >= VERSION_NTSC_1_0
&& !playerHasSharedViewport()
#else
@@ -2923,7 +2929,7 @@ s16 playerGetViewportHeight(void)
height = tmp / 2;
}
if (PLAYERCOUNT() == 2) {
if (LOCALPLAYERCOUNT() == 2) {
if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) {
height = tmp;
} else if (g_Vars.currentplayernum == 0 && IS8MB()) {
@@ -2959,7 +2965,7 @@ s16 playerGetViewportTop(void)
{
s16 top;
if (PLAYERCOUNT() >= 2
if (LOCALPLAYERCOUNT() >= 2
#if VERSION >= VERSION_NTSC_1_0
&& !playerHasSharedViewport()
#else
@@ -2970,12 +2976,12 @@ s16 playerGetViewportTop(void)
top = g_ViModes[g_ViRes].fulltop;
#if VERSION >= VERSION_NTSC_1_0
if (optionsGetScreenSplit() != SCREENSPLIT_VERTICAL || PLAYERCOUNT() != 2)
if (optionsGetScreenSplit() != SCREENSPLIT_VERTICAL || LOCALPLAYERCOUNT() != 2)
#else
if (optionsGetScreenSplit() != SCREENSPLIT_VERTICAL)
#endif
{
if (PLAYERCOUNT() == 2
if (LOCALPLAYERCOUNT() == 2
&& g_Vars.currentplayernum == 1
&& optionsGetScreenSplit() != SCREENSPLIT_VERTICAL
&& !g_Vars.fourmeg2player) {
+23
View File
@@ -10,6 +10,9 @@
#include "lib/rng.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
void playermgrInit(void)
{
@@ -60,6 +63,12 @@ void playermgrAllocatePlayers(s32 count)
playermgrAllocatePlayer(i);
}
#ifndef PLATFORM_N64
if (g_NetMode) {
netPlayersAllocate();
}
#endif
setCurrentPlayerNum(0);
g_Vars.bond = g_Vars.players[g_Vars.bondplayernum];
@@ -643,6 +652,12 @@ void playermgrAllocatePlayer(s32 index)
g_Vars.players[index]->disguised = false;
g_Vars.players[index]->dostartnewlife = false;
#ifndef PLATFORM_N64
g_Vars.players[index]->client = NULL;
g_Vars.players[index]->ucmd = (g_NetMode == NETMODE_SERVER) ? UCMD_FL_FORCEMASK : 0;
g_Vars.players[index]->isremote = false;
#endif
g_Vars.bondvisible = true;
g_Vars.bondcollisions = true;
}
@@ -806,6 +821,14 @@ void playermgrShuffle(void)
g_Vars.playerorder[i] = i;
}
#ifndef PLATFORM_N64
if (g_NetMode) {
// don't shuffle in netgames
// why is this a thing anyway?
return;
}
#endif
// Randomly swap numbers with later elements
for (i = 0; i < MAX_PLAYERS - 1; i++) {
s32 otherindex = random() % (MAX_PLAYERS - i);
+10
View File
@@ -39,6 +39,9 @@
#include "lib/lib_317f0.h"
#include "data.h"
#include "types.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
s16 *g_RoomPropListChunkIndexes;
struct roomproplistchunk *g_RoomPropListChunks;
@@ -1500,6 +1503,13 @@ bool currentPlayerInteract(bool eyespy)
prop = propFindForInteract(eyespy);
if (prop) {
#ifndef PLATFORM_N64
// if we aren't the authority, don't do anything
if (g_NetMode == NETMODE_CLIENT) {
return false;
}
#endif
switch (prop->type) {
case PROPTYPE_OBJ:
case PROPTYPE_WEAPON:
+18 -1
View File
@@ -77,6 +77,9 @@
#include "textures.h"
#include "types.h"
#include "string.h"
#ifndef PLATFORM_N64
#include "net/net.h"
#endif
void rng2SetSeed(u32 seed);
@@ -15354,6 +15357,13 @@ void objTakeGunfire(struct defaultobj *obj, f32 damage, struct coord *pos, s32 w
void objDamage(struct defaultobj *obj, f32 damage, struct coord *pos, s32 weaponnum, s32 playernum)
{
#ifndef PLATFORM_N64
// if we aren't the authority, don't do anything
if (g_NetMode == NETMODE_CLIENT) {
return;
}
#endif
// Store the attacker playernum into the object's "hidden" field
#if VERSION >= VERSION_NTSC_1_0
// ...but not for deployed laptop guns in multiplayer, because those bits
@@ -17472,6 +17482,13 @@ s32 objTestForPickup(struct prop *prop)
return TICKOP_NONE;
}
#ifndef PLATFORM_N64
// if we aren't the authority, don't do anything
if (g_NetMode == NETMODE_CLIENT) {
return TICKOP_NONE;
}
#endif
if (func0f085194(obj) && obj->type != OBJTYPE_HAT) {
if (obj->flags & OBJFLAG_UNCOLLECTABLE) {
return TICKOP_NONE;
@@ -20943,7 +20960,7 @@ Gfx *countdownTimerRender(Gfx *gdl)
u32 stack;
s32 viewright = viGetViewLeft() + (viGetViewWidth() >> 1);
s32 y = viGetViewTop() + viGetViewHeight() - 18;
s32 playercount = PLAYERCOUNT();
s32 playercount = LOCALPLAYERCOUNT();
char *fmt = ":\n";
if (playercount == 2) {
+3 -3
View File
@@ -338,7 +338,7 @@ void psTickChannel(s32 channelnum)
if (lvIsPaused()
|| (mpIsPaused() && (channel->flags2 & PSFLAG2_MPPAUSABLE))
|| (mpIsPaused() && PLAYERCOUNT() == 1)) {
|| (mpIsPaused() && LOCALPLAYERCOUNT() == 1)) {
channel->currentvol = -1;
newvol = 0;
}
@@ -1171,7 +1171,7 @@ s32 psCalculateVol(struct coord *pos, f32 dist1, f32 dist2, f32 dist3, RoomNum *
}
// Figure out which player is closest and store their distance in playerdist
for (i = 0; i < PLAYERCOUNT(); i++) {
for (i = 0; i < LOCALPLAYERCOUNT(); i++) {
struct player *player = g_Vars.players[i];
s32 camroom;
@@ -1283,7 +1283,7 @@ s32 psCalculatePan2(struct coord *pos, s32 arg1, f32 arg2, struct pschannel *cha
s32 degrees;
f32 f2;
if (PLAYERCOUNT() < 2) {
if (LOCALPLAYERCOUNT() < 2) {
struct coord *campos = &g_Vars.currentplayer->cam_pos;
f32 sp3c;
f32 sp38;
+2 -2
View File
@@ -252,7 +252,7 @@ Gfx *radarRender(Gfx *gdl)
tconfig = g_TexRadarConfigs;
playernum = g_Vars.currentplayernum;
playercount = PLAYERCOUNT();
playercount = LOCALPLAYERCOUNT();
if (g_Vars.mplayerisrunning) {
if (g_Vars.normmplayerisrunning && (g_MpSetup.options & MPOPTION_NORADAR)) {
@@ -331,7 +331,7 @@ Gfx *radarRender(Gfx *gdl)
gdl = func0f153134(gdl);
// Draw dots for human players
for (i = 0; i < playercount; i++) {
for (i = 0; i < PLAYERCOUNT(); i++) {
if (i != playernum) {
if (g_Vars.players[i]->isdead == false
&& (g_Vars.players[i]->prop->chr->hidden & CHRHFLAG_CLOAKED) == 0
+3 -3
View File
@@ -467,7 +467,7 @@ Gfx *sightDrawAimer(Gfx *gdl, s32 x, s32 y, s32 radius, s32 cornergap, u32 colou
#endif
// Draw the lines that span most of the viewport
if (PLAYERCOUNT() == 1) {
if (LOCALPLAYERCOUNT() == 1) {
gDPHudRectangle(gdl++, viewleft + 48, y, x - radius + 2, y);
gDPHudRectangle(gdl++, x + radius - 2, y, viewright - 49, y);
gDPHudRectangle(gdl++, x, viewtop + 10, x, y - radius + 2);
@@ -1262,7 +1262,7 @@ Gfx *sightDrawZoom(Gfx *gdl, bool sighton)
cornerheight *= frac;
}
if (PLAYERCOUNT() >= 2) {
if (LOCALPLAYERCOUNT() >= 2) {
cornerheight *= 2;
}
@@ -1522,7 +1522,7 @@ Gfx *sightDraw(Gfx *gdl, bool sighton, s32 sight)
}
#endif
if (PLAYERCOUNT() >= 2 && g_Vars.coopplayernum < 0 && g_Vars.antiplayernum < 0) {
if (LOCALPLAYERCOUNT() >= 2 && g_Vars.coopplayernum < 0 && g_Vars.antiplayernum < 0) {
sight = SIGHT_DEFAULT;
}
+4 -4
View File
@@ -53,7 +53,7 @@ void zbufAllocate(void)
if (IS4MB()) {
g_ZbufWidth = MAX(320, FBALLOC_WIDTH_LO);
if (g_Vars.normmplayerisrunning && PLAYERCOUNT() >= 2) {
if (g_Vars.normmplayerisrunning && LOCALPLAYERCOUNT() >= 2) {
g_ZbufHeight = MAX(220, FBALLOC_HEIGHT_LO) / 2;
} else {
g_ZbufHeight = MAX(220, FBALLOC_HEIGHT_LO);
@@ -61,7 +61,7 @@ void zbufAllocate(void)
} else {
g_ZbufWidth = MAX(640, FBALLOC_WIDTH_HI);
if (g_Vars.normmplayerisrunning && PLAYERCOUNT() >= 2) {
if (g_Vars.normmplayerisrunning && LOCALPLAYERCOUNT() >= 2) {
g_ZbufHeight = MAX(220, FBALLOC_HEIGHT_HI);
} else {
g_ZbufHeight = MAX(220, FBALLOC_HEIGHT_HI);
@@ -101,7 +101,7 @@ Gfx *zbufConfigureRdp(Gfx *gdl)
uintptr_t addr;
if (g_Vars.normmplayerisrunning
&& (g_Vars.currentplayernum >= 2 || (PLAYERCOUNT() == 2 && g_Vars.currentplayernum == 1))) {
&& (g_Vars.currentplayernum >= 2 || (LOCALPLAYERCOUNT() == 2 && g_Vars.currentplayernum == 1))) {
subamount = playerGetFbWidth() * playerGetFbHeight();
if (IS4MB() || optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) {
@@ -137,7 +137,7 @@ Gfx *zbufClear(Gfx *gdl)
gDPSetFillColor(gdl++, 0xfffcfffc);
gDPSetScissorFrac(gdl++, G_SC_NON_INTERLACE, 0, 0, playerGetFbWidth() * 4.0f, playerGetFbHeight() * 4.0f);
if (PLAYERCOUNT() <= 2) {
if (LOCALPLAYERCOUNT() <= 2) {
left = 0;
right = playerGetFbWidth() - 1;
} else if (g_Vars.currentplayernum == 0 || g_Vars.currentplayernum == 2) {
+10
View File
@@ -28,6 +28,7 @@
#define MAX_ROOMPROPLISTCHUNKS 256
#define MAX_SQUADRONS 16
#define MAX_TEAMS 8
#define MAX_PLAYERNAME 15
#define NUM_BOTDIFFS 6
#define NUM_CYCLEABLE_WEAPONS 45
@@ -695,6 +696,7 @@
#define CONTROLMODE_23 6
#define CONTROLMODE_24 7
#define CONTROLMODE_PC 8 // "pc port" controls; enabled in the .ini file
#define CONTROLMODE_NA 9 // dummy controls for remote players
#define COUNTDOWNTIMERREASON_AI 0x01
#define COUNTDOWNTIMERREASON_NOCONTROL 0x10
@@ -4682,6 +4684,8 @@ enum weaponnum {
#define BUTTON_ACCEPT BUTTON_ACCEPT_WPNFORWARD
#define BUTTON_WPNFORWARD BUTTON_ACCEPT_WPNFORWARD
#define LOCALPLAYERCOUNT() PLAYERCOUNT()
#else
// xbla behavior
@@ -4713,6 +4717,12 @@ enum weaponnum {
#define CROUCHMODE_TOGGLE 2 // press the crouch buttons to toggle stance
#define CROUCHMODE_TOGGLE_ANALOG (CROUCHMODE_ANALOG | CROUCHMODE_TOGGLE)
#define NETMODE_NONE 0
#define NETMODE_SERVER 1
#define NETMODE_CLIENT 2
#define LOCALPLAYERCOUNT() (g_NetMode ? 1 : PLAYERCOUNT())
#endif
#endif
+1
View File
@@ -548,6 +548,7 @@ extern s32 g_BlurFbCapTimer;
extern bool g_BlurFbDirty;
extern s32 g_TickRateDiv;
extern s32 g_MusicDisableMpDeath;
extern s32 g_NetMode;
#define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3]
#define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy)
+1
View File
@@ -203,6 +203,7 @@ bool chrAdjustPosForSpawn(f32 chrradius, struct coord *pos, RoomNum *rooms, f32
struct prop *chrSpawnAtCoord(s32 body, s32 head, struct coord *pos, RoomNum *room, f32 angle, u8 *ailist, u32 spawnflags);
bool chrIsPropPresetBlockingSightToTarget(struct chrdata *chr);
bool chrSetPos(struct chrdata *chr, struct coord *pos, RoomNum *rooms, f32 angle, bool findground);
bool chrMoveToPos(struct chrdata *chr, struct coord *pos, RoomNum *room, f32 angle, bool ignorebg);
bool chrCheckCoverOutOfSight(struct chrdata *chr, s32 covernum, bool soft);
s32 chrAssignCoverByCriteria(struct chrdata *chr, u16 arg1, s32 arg2);
+7 -1
View File
@@ -2820,6 +2820,9 @@ struct player {
/*0x1c72*/ s16 amdowntime; // for alt-modes, used like invdowntime and amdowntime
#ifndef PLATFORM_N64
/*0x1c74*/ f32 swivelpos[2];
/*0x1c84*/ u32 ucmd;
/*0x1c88*/ bool isremote;
/*0x1c8c*/ struct netclient *client;
#endif
};
@@ -4002,7 +4005,7 @@ struct gamefile {
};
struct mpchrconfig {
/*0x00*/ char name[15];
/*0x00*/ char name[MAX_PLAYERNAME];
/*0x0f*/ u8 mpheadnum;
/*0x10*/ u8 mpbodynum;
/*0x11*/ u8 team;
@@ -4046,6 +4049,9 @@ struct mpplayerconfig {
/*0x96*/ u8 newtitle;
/*0x97*/ u8 gunfuncs[6];
/*0x9d*/ u8 handicap;
#ifndef PLATFORM_N64
/*0x9e*/ struct netclient *client;
#endif
};
struct mpbotconfig {
+10 -10
View File
@@ -204,7 +204,7 @@ void viReset(s32 stagenum)
? FBALLOC_WIDTH_LO * FBALLOC_HEIGHT_LO * NUM_FRAMEBUFFERS
: FBALLOC_WIDTH_HI * FBALLOC_HEIGHT_HI * NUM_FRAMEBUFFERS;
if (IS4MB() && PLAYERCOUNT() == 2) {
if (IS4MB() && LOCALPLAYERCOUNT() == 2) {
// 4MB 2-player: The viewports are 110px tall
#if VERSION >= VERSION_NTSC_1_0
fbsize = FBALLOC_WIDTH_LO * (FBALLOC_HEIGHT_LO / 2) * NUM_FRAMEBUFFERS;
@@ -212,7 +212,7 @@ void viReset(s32 stagenum)
fbsize = SCREEN_320 * (SCREEN_240 / 2) * NUM_FRAMEBUFFERS;
#endif
g_Vars.fourmeg2player = true;
} else if ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) && PLAYERCOUNT() == 2) {
} else if ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) && LOCALPLAYERCOUNT() == 2) {
// PAL is using its correct size
fbsize = SCREEN_WIDTH_LO * SCREEN_HEIGHT_LO * NUM_FRAMEBUFFERS;
}
@@ -676,11 +676,11 @@ Gfx *viRenderViewportEdges(Gfx *gdl)
gDPSetFillColor(gdl++, GPACK_RGBA5551(0, 0, 0, 1) << 16 | GPACK_RGBA5551(0, 0, 0, 1));
#if VERSION >= VERSION_NTSC_1_0
if (PLAYERCOUNT() == 1
if (LOCALPLAYERCOUNT() == 1
|| ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0)
&& playerHasSharedViewport() && g_Vars.currentplayernum == 0))
#else
if (PLAYERCOUNT() == 1
if (LOCALPLAYERCOUNT() == 1
|| ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0)
&& (
(g_InCutscene && !g_MainIsEndscreen) || menuGetRoot() == MENUROOT_COOPCONTINUE
@@ -707,10 +707,10 @@ Gfx *viRenderViewportEdges(Gfx *gdl)
s32 bottomplayernum = 0;
s32 tmpplayernum = 0;
if (PLAYERCOUNT() == 2) {
if (LOCALPLAYERCOUNT() == 2) {
bottomplayernum = 1;
tmpplayernum = 1;
} else if (PLAYERCOUNT() >= 3) {
} else if (LOCALPLAYERCOUNT() >= 3) {
bottomplayernum = 2;
tmpplayernum = 2;
}
@@ -735,9 +735,9 @@ Gfx *viRenderViewportEdges(Gfx *gdl)
viGetWidth() - 1, g_Vars.players[tmpplayernum]->viewtop - 1);
gDPPipeSync(gdl++);
if (PLAYERCOUNT() >= 3 ||
(PLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || g_Vars.fourmeg2player))) {
if (PLAYERCOUNT() == 2) {
if (LOCALPLAYERCOUNT() >= 3 ||
(LOCALPLAYERCOUNT() == 2 && (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL || g_Vars.fourmeg2player))) {
if (LOCALPLAYERCOUNT() == 2) {
tmpplayernum = 0;
}
@@ -748,7 +748,7 @@ Gfx *viRenderViewportEdges(Gfx *gdl)
gDPPipeSync(gdl++);
}
if (PLAYERCOUNT() == 3) {
if (LOCALPLAYERCOUNT() == 3) {
// Blank square in P4 spot
gDPFillRectangle(gdl++,
g_Vars.players[tmpplayernum]->viewleft + g_Vars.players[tmpplayernum]->viewwidth + 1, g_Vars.players[tmpplayernum]->viewtop,