mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-13 00:24:24 +02:00
#142 network multiplayer basic protocol and routine (WIP)
This commit is contained in:
10
src/core.h
10
src/core.h
@@ -15,6 +15,7 @@
|
||||
#define _GAPI_GL 1
|
||||
//#define _GAPI_D3D9 1
|
||||
//#define _GAPI_VULKAN 1
|
||||
//#define _NAPI_SOCKET
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -324,6 +325,12 @@ namespace Core {
|
||||
#include "input.h"
|
||||
#include "sound.h"
|
||||
|
||||
#if defined(_NAPI_SOCKET)
|
||||
#include "napi_socket.h"
|
||||
#else
|
||||
#include "napi_dummy.h"
|
||||
#endif
|
||||
|
||||
#define MAX_LIGHTS 4
|
||||
#define MAX_RENDER_BUFFERS 32
|
||||
#define MAX_CONTACTS 15
|
||||
@@ -622,6 +629,7 @@ namespace Core {
|
||||
|
||||
Input::init();
|
||||
Sound::init();
|
||||
NAPI::init();
|
||||
|
||||
GAPI::init();
|
||||
|
||||
@@ -770,7 +778,7 @@ namespace Core {
|
||||
delete ditherTex;
|
||||
|
||||
GAPI::deinit();
|
||||
|
||||
NAPI::deinit();
|
||||
Sound::deinit();
|
||||
}
|
||||
|
||||
|
@@ -173,6 +173,7 @@ namespace Game {
|
||||
|
||||
void updateTick() {
|
||||
Input::update();
|
||||
Network::update();
|
||||
|
||||
cheatControl(Input::lastState[0]);
|
||||
|
||||
|
11
src/lara.h
11
src/lara.h
@@ -320,6 +320,8 @@ struct Lara : Character {
|
||||
|
||||
float hitTimer;
|
||||
|
||||
int32 networkInput;
|
||||
|
||||
#ifdef _DEBUG
|
||||
//uint16 *dbgBoxes;
|
||||
//int dbgBoxesCount;
|
||||
@@ -494,8 +496,9 @@ struct Lara : Character {
|
||||
Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), dozy(false), wpnCurrent(TR::Entity::NONE), wpnNext(TR::Entity::NONE), braid(NULL) {
|
||||
camera = new Camera(game, this);
|
||||
|
||||
itemHolster = TR::Entity::NONE;
|
||||
hitTimer = 0.0f;
|
||||
itemHolster = TR::Entity::NONE;
|
||||
hitTimer = 0.0f;
|
||||
networkInput = -1;
|
||||
|
||||
if (level->extra.laraSkin > -1)
|
||||
level->entities[entity].modelIndex = level->extra.laraSkin + 1;
|
||||
@@ -2893,6 +2896,10 @@ struct Lara : Character {
|
||||
|
||||
virtual int getInput() { // TODO: updateInput
|
||||
if (level->isCutsceneLevel()) return 0;
|
||||
|
||||
if (networkInput != -1)
|
||||
return networkInput;
|
||||
|
||||
input = 0;
|
||||
int pid = camera->cameraIndex;
|
||||
|
||||
|
12
src/level.h
12
src/level.h
@@ -11,6 +11,7 @@
|
||||
#include "trigger.h"
|
||||
#include "inventory.h"
|
||||
#include "savegame.h"
|
||||
#include "network.h"
|
||||
|
||||
#if defined(_DEBUG) && defined(_GAPI_GL) && !defined(_GAPI_GLES)
|
||||
#define DEBUG_RENDER
|
||||
@@ -872,10 +873,14 @@ struct Level : IGame {
|
||||
loadSlot = -1;
|
||||
}
|
||||
|
||||
Network::start(this);
|
||||
|
||||
Core::resetTime();
|
||||
}
|
||||
|
||||
virtual ~Level() {
|
||||
Network::stop();
|
||||
|
||||
for (int i = 0; i < level.entitiesCount; i++)
|
||||
delete (Controller*)level.entities[i].controller;
|
||||
|
||||
@@ -1812,6 +1817,13 @@ struct Level : IGame {
|
||||
sndWater->setVolume(volWater, 0.2f);
|
||||
if (sndTrack && sndTrack->volumeTarget != volTrack)
|
||||
sndTrack->setVolume(volTrack, 0.2f);
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (Input::down[ikJ]) {
|
||||
Network::sayHello();
|
||||
Input::down[ikJ] = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void updateEffect() {
|
||||
|
17
src/napi_dummy.h
Normal file
17
src/napi_dummy.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef H_NAPI_DUMMY
|
||||
#define H_NAPI_DUMMYT
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace NAPI {
|
||||
typedef int Peer;
|
||||
|
||||
void init() {}
|
||||
void deinit() {}
|
||||
void listen(uint16 port) {}
|
||||
int send(const Peer &to, const void *data, int size) { return 0; }
|
||||
int recv(Peer &from, void *data, int size) { return 0; }
|
||||
void broadcast(const void *data, int size) {}
|
||||
}
|
||||
|
||||
#endif
|
103
src/napi_socket.h
Normal file
103
src/napi_socket.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef H_NAPI_SOCKET
|
||||
#define H_NAPI_SOCKET
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef _OS_WIN
|
||||
#include "winsock.h"
|
||||
#endif
|
||||
|
||||
namespace NAPI {
|
||||
|
||||
struct Peer {
|
||||
uint16 port;
|
||||
uint32 ip;
|
||||
|
||||
inline bool operator == (const Peer &peer) const {
|
||||
return port == peer.port && ip == peer.ip;
|
||||
}
|
||||
};
|
||||
|
||||
SOCKET sock;
|
||||
sockaddr_in addr;
|
||||
uint16 port;
|
||||
|
||||
void init() {
|
||||
sock = INVALID_SOCKET;
|
||||
|
||||
WSAData wData;
|
||||
WSAStartup(0x0101, &wData);
|
||||
}
|
||||
|
||||
void deinit() {
|
||||
if (sock != INVALID_SOCKET) {
|
||||
shutdown(sock, 1);
|
||||
#ifdef _OS_WIN
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void listen(uint16 port) {
|
||||
NAPI::port = port;
|
||||
|
||||
if (sock != INVALID_SOCKET) return;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
LOG("! network: failed to create socket\n");
|
||||
sock = INVALID_SOCKET;
|
||||
return;
|
||||
}
|
||||
|
||||
u_long on = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &on) < 0) {
|
||||
LOG("! network: failed to set non-blocking mode\n");
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (bind(sock, (sockaddr*)&addr, sizeof(addr)))
|
||||
LOG("! network: unable to bind socket on port (%d)\n", (int)port);
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on)))
|
||||
LOG("! network: unable to enable broadcasting\n");
|
||||
}
|
||||
|
||||
int send(const Peer &to, const void *data, int size) {
|
||||
if (sock == INVALID_SOCKET) return false;
|
||||
|
||||
addr.sin_addr.s_addr = to.ip;
|
||||
addr.sin_port = to.port;
|
||||
return sendto(sock, (const char*)data, size, 0, (sockaddr*)&addr, sizeof(addr));
|
||||
}
|
||||
|
||||
int recv(Peer &from, void *data, int size) {
|
||||
if (sock == INVALID_SOCKET) return false;
|
||||
|
||||
int i = sizeof(addr);
|
||||
int count = recvfrom(sock, (char*)data, size, 0, (sockaddr*)&addr, &i);
|
||||
if (count > 0) {
|
||||
from.ip = addr.sin_addr.s_addr;
|
||||
from.port = addr.sin_port;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void broadcast(const void *data, int size) {
|
||||
Peer peer;
|
||||
peer.ip = INADDR_BROADCAST;
|
||||
peer.port = htons(port);
|
||||
send(peer, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
354
src/network.h
Normal file
354
src/network.h
Normal file
@@ -0,0 +1,354 @@
|
||||
#ifndef H_NET
|
||||
#define H_NET
|
||||
|
||||
#include "core.h"
|
||||
#include "utils.h"
|
||||
#include "format.h"
|
||||
#include "controller.h"
|
||||
#include "ui.h"
|
||||
|
||||
#define NET_PROTOCOL 1
|
||||
#define NET_PORT 21468
|
||||
|
||||
#define NET_PING_TIMEOUT ( 1000 * 10 )
|
||||
#define NET_PING_PERIOD ( 1000 * 3 )
|
||||
#define NET_SYMC_INPUT_PERIOD ( 1000 / 25 )
|
||||
#define NET_SYMC_STATE_PERIOD ( 1000 / 1000 )
|
||||
|
||||
namespace Network {
|
||||
|
||||
struct Packet {
|
||||
enum Type {
|
||||
HELLO, INFO, PING, PONG, JOIN, ACCEPT, REJECT, INPUT, STATE,
|
||||
};
|
||||
|
||||
uint16 type;
|
||||
uint16 id;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint8 protocol;
|
||||
uint8 game;
|
||||
} hello;
|
||||
|
||||
struct {
|
||||
str16 name;
|
||||
uint8 level;
|
||||
uint8 players;
|
||||
struct {
|
||||
uint16 secure:1;
|
||||
} flags;
|
||||
} info;
|
||||
|
||||
struct {
|
||||
str16 nick;
|
||||
str16 pass;
|
||||
} join;
|
||||
|
||||
struct {
|
||||
uint16 id;
|
||||
uint8 level;
|
||||
uint8 roomIndex;
|
||||
int16 posX;
|
||||
int16 posY;
|
||||
int16 posZ;
|
||||
int16 angle;
|
||||
} accept;
|
||||
|
||||
struct {
|
||||
uint16 reason;
|
||||
} reject;
|
||||
|
||||
struct {
|
||||
uint16 mask;
|
||||
} input;
|
||||
|
||||
struct {
|
||||
uint8 roomIndex;
|
||||
uint8 reserved;
|
||||
int16 pos[3];
|
||||
int16 angle[2];
|
||||
uint8 frame;
|
||||
uint8 stand;
|
||||
uint16 animIndex;
|
||||
} state;
|
||||
};
|
||||
|
||||
int getSize() const {
|
||||
const int sizes[] = {
|
||||
sizeof(hello),
|
||||
sizeof(info),
|
||||
0,
|
||||
0,
|
||||
sizeof(join),
|
||||
sizeof(accept),
|
||||
sizeof(reject),
|
||||
sizeof(input),
|
||||
sizeof(state),
|
||||
};
|
||||
|
||||
if (type >= 0 && type < COUNT(sizes))
|
||||
return 2 + 2 + sizes[type];
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IGame *game;
|
||||
|
||||
struct Player {
|
||||
NAPI::Peer peer;
|
||||
int pingTime;
|
||||
int pingIndex;
|
||||
Controller *controller;
|
||||
};
|
||||
|
||||
Array<Player> players;
|
||||
|
||||
int syncInputTime;
|
||||
int syncStateTime;
|
||||
|
||||
void start(IGame *game) {
|
||||
Network::game = game;
|
||||
NAPI::listen(NET_PORT);
|
||||
syncInputTime = syncStateTime = osGetTime();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
players.clear();
|
||||
}
|
||||
|
||||
bool sendPacket(const NAPI::Peer &to, const Packet &packet) {
|
||||
return NAPI::send(to, &packet, packet.getSize()) > 0;
|
||||
}
|
||||
|
||||
bool recvPacket(NAPI::Peer &from, Packet &packet) {
|
||||
int count = NAPI::recv(from, &packet, sizeof(packet));
|
||||
if (count > 0) {
|
||||
if (count != packet.getSize()) {
|
||||
ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void sayHello() {
|
||||
Packet packet;
|
||||
packet.type = Packet::HELLO;
|
||||
packet.hello.protocol = NET_PROTOCOL;
|
||||
packet.hello.game = game->getLevel()->version & TR::VER_VERSION;
|
||||
|
||||
NAPI::broadcast(&packet, packet.getSize());
|
||||
}
|
||||
|
||||
void joinGame(const NAPI::Peer &peer) {
|
||||
Packet packet;
|
||||
packet.type = Packet::JOIN;
|
||||
packet.join.nick = "Player_2";
|
||||
packet.join.pass = "";
|
||||
LOG("join game\n");
|
||||
sendPacket(peer, packet);
|
||||
}
|
||||
|
||||
void pingPlayers(int time) {
|
||||
int i = 0;
|
||||
while (i < players.length) {
|
||||
int delta = time - players[i].pingTime;
|
||||
|
||||
if (delta > NET_PING_TIMEOUT) {
|
||||
players.removeFast(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (delta > NET_PING_PERIOD) {
|
||||
Packet packet;
|
||||
packet.type = Packet::PING;
|
||||
sendPacket(players[i].peer, packet);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void syncInput(int time) {
|
||||
Lara *lara = (Lara*)game->getLara();
|
||||
if (!lara) return;
|
||||
|
||||
if ((time - syncInputTime) < NET_SYMC_INPUT_PERIOD)
|
||||
return;
|
||||
|
||||
Packet packet;
|
||||
packet.type = Packet::INPUT;
|
||||
packet.input.mask = lara->getInput();
|
||||
|
||||
for (int i = 0; i < players.length; i++)
|
||||
sendPacket(players[i].peer, packet);
|
||||
|
||||
syncInputTime = time;
|
||||
}
|
||||
|
||||
void syncState(int time) {
|
||||
if ((time - syncStateTime) < NET_SYMC_STATE_PERIOD)
|
||||
return;
|
||||
// TODO
|
||||
syncStateTime = time;
|
||||
}
|
||||
|
||||
Player* getPlayerByPeer(const NAPI::Peer &peer) {
|
||||
for (int i = 0; i < players.length; i++)
|
||||
if (players[i].peer == peer) {
|
||||
return &players[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void getSpawnPoint(uint8 &roomIndex, vec3 &pos, float &angle) {
|
||||
Controller *lara = game->getLara();
|
||||
roomIndex = lara->getRoomIndex();
|
||||
pos = lara->getPos();
|
||||
angle = normalizeAngle(lara->angle.y); // 0..2PI
|
||||
}
|
||||
|
||||
void update() {
|
||||
int count;
|
||||
NAPI::Peer from;
|
||||
Packet packet, response;
|
||||
|
||||
int time = osGetTime();
|
||||
|
||||
while ( (count = recvPacket(from, packet)) > 0 ) {
|
||||
Player *player = getPlayerByPeer(from);
|
||||
if (player)
|
||||
player->pingTime = time;
|
||||
|
||||
switch (packet.type) {
|
||||
case Packet::HELLO :
|
||||
if (game->getLevel()->isTitle())
|
||||
break;
|
||||
|
||||
LOG("recv HELLO\n");
|
||||
if (packet.hello.game != (game->getLevel()->version & TR::VER_VERSION))
|
||||
break;
|
||||
if (packet.hello.protocol != NET_PROTOCOL)
|
||||
break;
|
||||
LOG("send INFO\n");
|
||||
response.type = Packet::INFO;
|
||||
response.info.name = "MultiOpenLara";
|
||||
response.info.level = game->getLevel()->id;
|
||||
response.info.players = players.length + 1;
|
||||
response.info.flags.secure = false;
|
||||
|
||||
sendPacket(from, response);
|
||||
|
||||
break;
|
||||
|
||||
case Packet::INFO : {
|
||||
LOG("recv INFO\n");
|
||||
char buf[sizeof(packet.info.name) + 1];
|
||||
packet.info.name.get(buf);
|
||||
LOG("name: %s\n", buf);
|
||||
joinGame(from);
|
||||
break;
|
||||
}
|
||||
|
||||
case Packet::PING :
|
||||
if (player) {
|
||||
response.type = Packet::PONG;
|
||||
sendPacket(from, response);
|
||||
}
|
||||
break;
|
||||
|
||||
case Packet::PONG :
|
||||
break;
|
||||
|
||||
case Packet::JOIN :
|
||||
if (!player) {
|
||||
uint8 roomIndex;
|
||||
vec3 pos;
|
||||
float angle;
|
||||
|
||||
getSpawnPoint(roomIndex, pos, angle);
|
||||
|
||||
Player newPlayer;
|
||||
newPlayer.peer = from;
|
||||
newPlayer.pingIndex = 0;
|
||||
newPlayer.pingTime = time;
|
||||
newPlayer.controller = game->addEntity(TR::Entity::LARA, roomIndex, pos, angle);
|
||||
players.push(newPlayer);
|
||||
|
||||
((Lara*)newPlayer.controller)->networkInput = 0;
|
||||
|
||||
char buf[32];
|
||||
packet.join.nick.get(buf);
|
||||
LOG("Player %s joined\n", buf);
|
||||
|
||||
ASSERT(newPlayer.controller);
|
||||
|
||||
TR::Room &room = game->getLevel()->rooms[roomIndex];
|
||||
vec3 offset = pos - room.getOffset();
|
||||
|
||||
response.type = Packet::ACCEPT;
|
||||
response.accept.id = 0;
|
||||
response.accept.level = game->getLevel()->id;
|
||||
response.accept.roomIndex = roomIndex;
|
||||
response.accept.posX = int16(offset.x);
|
||||
response.accept.posY = int16(offset.y);
|
||||
response.accept.posZ = int16(offset.z);
|
||||
response.accept.angle = int16(angle * RAD2DEG);
|
||||
|
||||
sendPacket(from, response);
|
||||
}
|
||||
break;
|
||||
|
||||
case Packet::ACCEPT : {
|
||||
LOG("accept!\n");
|
||||
game->loadLevel(TR::LevelID(packet.accept.level));
|
||||
inventory->toggle();
|
||||
break;
|
||||
}
|
||||
|
||||
case Packet::REJECT :
|
||||
break;
|
||||
|
||||
case Packet::INPUT :
|
||||
if (game->getLevel()->isTitle())
|
||||
break;
|
||||
|
||||
if (!player) {
|
||||
uint8 roomIndex;
|
||||
vec3 pos;
|
||||
float angle;
|
||||
|
||||
getSpawnPoint(roomIndex, pos, angle);
|
||||
|
||||
Player newPlayer;
|
||||
newPlayer.peer = from;
|
||||
newPlayer.pingIndex = 0;
|
||||
newPlayer.pingTime = time;
|
||||
newPlayer.controller = game->addEntity(TR::Entity::LARA, roomIndex, pos, angle);
|
||||
players.push(newPlayer);
|
||||
|
||||
((Lara*)newPlayer.controller)->networkInput = 0;
|
||||
|
||||
player = getPlayerByPeer(from);
|
||||
}
|
||||
|
||||
if (player) {
|
||||
((Lara*)player->controller)->networkInput = packet.input.mask;
|
||||
}
|
||||
break;
|
||||
|
||||
case Packet::STATE :
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pingPlayers(time);
|
||||
syncInput(time);
|
||||
syncState(time);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -106,7 +106,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>openvr_api.lib;d3d9.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>wsock32.lib;openvr_api.lib;d3d9.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Editor|Win32'">
|
||||
@@ -149,7 +149,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wcrt.lib;openvr_api.lib;d3d9.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>wcrt.lib;wsock32.lib;openvr_api.lib;d3d9.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
@@ -179,7 +179,7 @@
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>wcrt.lib;openvr_api.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>wcrt.lib;wsock32.lib;openvr_api.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
@@ -199,6 +199,7 @@
|
||||
<ClInclude Include="..\..\collision.h" />
|
||||
<ClInclude Include="..\..\controller.h" />
|
||||
<ClInclude Include="..\..\core.h" />
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\gapi_d3d9.h" />
|
||||
<ClInclude Include="..\..\gapi_gl.h" />
|
||||
<ClInclude Include="..\..\gapi_gu.h" />
|
||||
@@ -209,12 +210,14 @@
|
||||
<ClInclude Include="..\..\frustum.h" />
|
||||
<ClInclude Include="..\..\game.h" />
|
||||
<ClInclude Include="..\..\gameflow.h" />
|
||||
<ClInclude Include="..\..\libs\tinf\tinf.h" />
|
||||
<ClInclude Include="..\..\ui.h" />
|
||||
<ClInclude Include="..\..\napi_dummy.h" />
|
||||
<ClInclude Include="..\..\napi_socket.h" />
|
||||
<ClInclude Include="..\..\network.h" />
|
||||
<ClInclude Include="..\..\input.h" />
|
||||
<ClInclude Include="..\..\inventory.h" />
|
||||
<ClInclude Include="..\..\lara.h" />
|
||||
<ClInclude Include="..\..\level.h" />
|
||||
<ClInclude Include="..\..\libs\tinf\tinf.h" />
|
||||
<ClInclude Include="..\..\libs\minimp3\libc.h" />
|
||||
<ClInclude Include="..\..\libs\minimp3\minimp3.h" />
|
||||
<ClInclude Include="..\..\mesh.h" />
|
||||
@@ -223,9 +226,9 @@
|
||||
<ClInclude Include="..\..\sound.h" />
|
||||
<ClInclude Include="..\..\sprite.h" />
|
||||
<ClInclude Include="..\..\texture.h" />
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\trigger.h" />
|
||||
<ClInclude Include="..\..\utils.h" />
|
||||
<ClInclude Include="..\..\ui.h" />
|
||||
<ClInclude Include="..\..\video.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@@ -54,6 +54,9 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\video.h" />
|
||||
<ClInclude Include="..\..\savegame.h" />
|
||||
<ClInclude Include="..\..\network.h" />
|
||||
<ClInclude Include="..\..\napi_socket.h" />
|
||||
<ClInclude Include="..\..\napi_dummy.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\shaders\filter.glsl">
|
||||
|
24
src/utils.h
24
src/utils.h
@@ -1638,6 +1638,27 @@ namespace String {
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <int N>
|
||||
struct FixedStr {
|
||||
char data[N];
|
||||
|
||||
void get(char *dst) {
|
||||
memcpy(dst, data, sizeof(data));
|
||||
dst[sizeof(data)] = 0;
|
||||
}
|
||||
|
||||
FixedStr<N>& operator = (const char *str) {
|
||||
int len = min(sizeof(data), strlen(str));
|
||||
memset(data, 0, sizeof(data));
|
||||
memcpy(data, str, len);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
typedef FixedStr<16> str16;
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Array {
|
||||
int capacity;
|
||||
@@ -1675,7 +1696,8 @@ struct Array {
|
||||
}
|
||||
|
||||
void removeFast(int index) {
|
||||
(*this)[index] = (*this)[--length];
|
||||
(*this)[index] = (*this)[length - 1];
|
||||
length--;
|
||||
}
|
||||
|
||||
void remove(int index) {
|
||||
|
Reference in New Issue
Block a user