forked from Ports/perfect_dark
port: net: basic networking with barely working position sync
This commit is contained in:
1 parent
0a7faf2409
commit
6fc99e4c23
47 files changed
+7500
-101
No files matched your search
+1
-1
@@ -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
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
#define ENET_IMPLEMENTATION 1
|
||||
#define ENET_NO_PRAGMA_LINK 1
|
||||
#include "external/enet.h"
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include "external/minimp3.h"
|
||||
Vendored
+5098
File diff suppressed because it is too large.
Load diff
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "mixer.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include "external/minimp3.h"
|
||||
|
||||
#ifdef __SSE4_1__
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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
@@ -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
@@ -319,7 +319,7 @@ Gfx *nbombCreateGdl(void)
|
||||
Gfx *gdl;
|
||||
s32 index = 0;
|
||||
|
||||
if (PLAYERCOUNT() >= 2) {
|
||||
if (LOCALPLAYERCOUNT() >= 2) {
|
||||
index = 1;
|
||||
}
|
||||
|
||||
|
||||
+15
-9
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user