Merge pull request #55 from dongresource/combat1

Implemented combat, drops, crates and the guide changer
This commit is contained in:
CPunch 2020-08-28 19:11:56 -05:00 committed by GitHub
commit 50431024c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 339 additions and 53 deletions

6
.vimrc
View File

@ -1,9 +1,9 @@
" vim configuration file " vim configuration file
" you will need to put 'set exrc' and 'set secure' into your main .vimrc file, " You will need to put 'set exrc' and 'set secure' into your main .vimrc file,
" in which case this file will be loaded automatically, but *only* if you " in which case this file will be loaded automatically, but *only* if you
" start vim in this dir, or you can just load it directly with ':so .vimrc' " start vim in this dir. Alternatively you can just load it directly with
" every time. " ':so .vimrc' every time.
set tabstop=4 set tabstop=4
set shiftwidth=4 set shiftwidth=4
set expandtab set expandtab

View File

@ -1,7 +1,7 @@
CC=clang CC=clang
CXX=clang++ CXX=clang++
# -w suppresses all warnings (the part that's commented out helps me find memory leaks, it ruins performance though!) # -w suppresses all warnings (the part that's commented out helps me find memory leaks, it ruins performance though!)
CCFLAGS=-O3 #-g3 -fsanitize=address CFLAGS=-O3 #-g3 -fsanitize=address
CXXFLAGS=-Wall -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address CXXFLAGS=-Wall -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
LDFLAGS=-lpthread -ldl LDFLAGS=-lpthread -ldl
# specifies the name of our exectuable # specifies the name of our exectuable
@ -14,22 +14,23 @@ PROTOCOL_VERSION?=104
# Windows-specific # Windows-specific
WIN_CC=x86_64-w64-mingw32-gcc WIN_CC=x86_64-w64-mingw32-gcc
WIN_CXX=x86_64-w64-mingw32-g++ WIN_CXX=x86_64-w64-mingw32-g++
WIN_CCFLAGS=-O3 #-g3 -fsanitize=address WIN_CFLAGS=-O3 #-g3 -fsanitize=address
WIN_CXXFLAGS=-Wall -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address WIN_CXXFLAGS=-Wall -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
WIN_LDFLAGS=-static -lws2_32 -lwsock32 WIN_LDFLAGS=-static -lws2_32 -lwsock32
WIN_SERVER=bin/winfusion.exe WIN_SERVER=bin/winfusion.exe
CC_SRC=\ CSRC=\
src/contrib/bcrypt/bcrypt.c\ src/contrib/bcrypt/bcrypt.c\
src/contrib/bcrypt/crypt_blowfish.c\ src/contrib/bcrypt/crypt_blowfish.c\
src/contrib/bcrypt/crypt_gensalt.c\ src/contrib/bcrypt/crypt_gensalt.c\
src/contrib/bcrypt/wrapper.c\ src/contrib/bcrypt/wrapper.c\
src/contrib/sqlite/sqlite3.c\ src/contrib/sqlite/sqlite3.c\
CXX_SRC=\ CXXSRC=\
src/contrib/sqlite/sqlite3pp.cpp\ src/contrib/sqlite/sqlite3pp.cpp\
src/contrib/sqlite/sqlite3ppext.cpp\ src/contrib/sqlite/sqlite3ppext.cpp\
src/ChatManager.cpp\ src/ChatManager.cpp\
src/CombatManager.cpp\
src/CNLoginServer.cpp\ src/CNLoginServer.cpp\
src/CNProtocol.cpp\ src/CNProtocol.cpp\
src/CNShardServer.cpp\ src/CNShardServer.cpp\
@ -47,7 +48,7 @@ CXX_SRC=\
src/settings.cpp\ src/settings.cpp\
# headers (for timestamp purposes) # headers (for timestamp purposes)
CC_HDR=\ CHDR=\
src/contrib/bcrypt/bcrypt.h\ src/contrib/bcrypt/bcrypt.h\
src/contrib/bcrypt/crypt_blowfish.h\ src/contrib/bcrypt/crypt_blowfish.h\
src/contrib/bcrypt/crypt_gensalt.h\ src/contrib/bcrypt/crypt_gensalt.h\
@ -56,13 +57,14 @@ CC_HDR=\
src/contrib/sqlite/sqlite3.h\ src/contrib/sqlite/sqlite3.h\
src/contrib/sqlite/sqlite3ext.h\ src/contrib/sqlite/sqlite3ext.h\
CXX_HDR=\ CXXHDR=\
src/contrib/bcrypt/BCrypt.hpp\ src/contrib/bcrypt/BCrypt.hpp\
src/contrib/sqlite/sqlite3pp.h\ src/contrib/sqlite/sqlite3pp.h\
src/contrib/sqlite/sqlite3ppext.h\ src/contrib/sqlite/sqlite3ppext.h\
src/contrib/INIReader.hpp\ src/contrib/INIReader.hpp\
src/contrib/JSON.hpp\ src/contrib/JSON.hpp\
src/ChatManager.hpp\ src/ChatManager.hpp\
src/CombatManager.hpp\
src/CNLoginServer.hpp\ src/CNLoginServer.hpp\
src/CNProtocol.hpp\ src/CNProtocol.hpp\
src/CNShardServer.hpp\ src/CNShardServer.hpp\
@ -80,10 +82,10 @@ CXX_HDR=\
src/PlayerManager.hpp\ src/PlayerManager.hpp\
src/settings.hpp\ src/settings.hpp\
CC_OBJ=$(CC_SRC:.c=.o) COBJ=$(CSRC:.c=.o)
CXX_OBJ=$(CXX_SRC:.cpp=.o) CXXOBJ=$(CXXSRC:.cpp=.o)
OBJ=$(CC_OBJ) $(CXX_OBJ) OBJ=$(COBJ) $(CXXOBJ)
all: $(SERVER) all: $(SERVER)
@ -92,18 +94,20 @@ windows: $(SERVER)
# assign Windows-specific values if targeting Windows # assign Windows-specific values if targeting Windows
windows : CC=$(WIN_CC) windows : CC=$(WIN_CC)
windows : CXX=$(WIN_CXX) windows : CXX=$(WIN_CXX)
windows : CCFLAGS=$(WIN_CCFLAGS) windows : CFLAGS=$(WIN_CFLAGS)
windows : CXXFLAGS=$(WIN_CXXFLAGS) windows : CXXFLAGS=$(WIN_CXXFLAGS)
windows : LDFLAGS=$(WIN_LDFLAGS) windows : LDFLAGS=$(WIN_LDFLAGS)
windows : SERVER=$(WIN_SERVER) windows : SERVER=$(WIN_SERVER)
$(CC_OBJ): %.o: %.c $(CC_HDR) .SUFFIX: .o .c .cpp .hpp
$(CC) -c $(CCFLAGS) -o $@ $<
$(CXX_OBJ): %.o: %.cpp $(CXX_HDR) .c.o: $(CHDR)
$(CC) -c $(CFLAGS) -o $@ $<
.cpp.o: $(CXXHDR)
$(CXX) -c $(CXXFLAGS) -o $@ $< $(CXX) -c $(CXXFLAGS) -o $@ $<
$(SERVER): $(OBJ) $(CC_HDR) $(CXX_HDR) $(SERVER): $(OBJ) $(CHDR) $(CXXHDR)
mkdir -p bin mkdir -p bin
$(CXX) $(OBJ) $(LDFLAGS) -o $(SERVER) $(CXX) $(OBJ) $(LDFLAGS) -o $(SERVER)

View File

@ -16,7 +16,7 @@ tl;dr:
From then on, any time you want to run the "game": From then on, any time you want to run the "game":
3. Run `OpenFusionRelease/winfusion.exe` 3. Run `Server/winfusion.exe`
4. Run `FreeClient/OpenFusionClient.exe` 4. Run `FreeClient/OpenFusionClient.exe`
Currently the client by default connects to a public server hosted by Cake. Change the loginInfo.php to point to your own server if you want to host your own. Currently the client by default connects to a public server hosted by Cake. Change the loginInfo.php to point to your own server if you want to host your own.
@ -61,7 +61,7 @@ When the player clicks "ENTER THE GAME" (or completes the tutorial), the login s
## Configuration ## Configuration
You can change the ports the FusionFall server listens on in `OpenFusion/config.ini`. Make sure the login server port is in sync with `loginInfo.php`. You can change the ports the FusionFall server listens on in `Server/config.ini`. Make sure the login server port is in sync with `loginInfo.php`.
The shard port needs no such synchronization. The shard port needs no such synchronization.
You can also configure the distance at which you'll be able to see other players, though by default it's already as high as you'll want it. You can also configure the distance at which you'll be able to see other players, though by default it's already as high as you'll want it.
@ -103,11 +103,11 @@ To make your landwalking experience more pleasant, you can make use of a few adm
* `/goto` is useful for more precise teleportation (ie. for getting into Infected Zones, etc.). * `/goto` is useful for more precise teleportation (ie. for getting into Infected Zones, etc.).
### Item commands ### Item commands
* /itemN [type] [itemId] [amount] * `/itemN [type] [itemId] [amount]`
(Refer to the [item list](https://docs.google.com/spreadsheets/d/1mpoJ9iTHl_xLI4wQ_9UvIDYNcsDYscdkyaGizs43TCg/)) (Refer to the [item list](https://docs.google.com/spreadsheets/d/1mpoJ9iTHl_xLI4wQ_9UvIDYNcsDYscdkyaGizs43TCg/))
### Nano commands ### Nano commands
* /nano [id] (1-36) * `/nano [id] (1-36)`
* /nano_equip [id] (1-36) [slot] (0-2) * `/nano_equip [id] (1-36) [slot] (0-2)`
* /nano_unequip [slot] (0-2) * `/nano_unequip [slot] (0-2)`
* /nano_active [slot] (0-2) * `/nano_active [slot] (0-2)`

View File

@ -23,9 +23,9 @@ npcdistance=16000
# little message players see when they enter the game # little message players see when they enter the game
motd=Welcome to OpenFusion! motd=Welcome to OpenFusion!
# NPC json data # NPC json data
npcdata=NPCs.json npcdata=data/NPCs.json
# warp target json data # warp target json data
warpdata=warps.json warpdata=data/warps.json
# is everyone a GM? # is everyone a GM?
gm=true gm=true

View File

@ -123,23 +123,25 @@ void CNSocket::sendPacket(void* buf, uint32_t type, size_t size) {
if (!alive) if (!alive)
return; return;
int tmpSize = size + sizeof(uint32_t); size_t bodysize = size + sizeof(uint32_t);
uint8_t* tmpBuf = (uint8_t*)xmalloc(tmpSize); uint8_t* fullpkt = (uint8_t*)xmalloc(bodysize+4);
uint8_t* body = fullpkt+4;
memcpy(fullpkt, (void*)&bodysize, 4);
// copy packet type to the front of the buffer & then the actual buffer // copy packet type to the front of the buffer & then the actual buffer
memcpy(tmpBuf, (void*)&type, sizeof(uint32_t)); memcpy(body, (void*)&type, sizeof(uint32_t));
memcpy(tmpBuf+sizeof(uint32_t), buf, size); memcpy(body+sizeof(uint32_t), buf, size);
// encrypt the packet // encrypt the packet
switch (activeKey) { switch (activeKey) {
case SOCKETKEY_E: case SOCKETKEY_E:
CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&EKey), tmpSize); CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&EKey), bodysize);
break; break;
case SOCKETKEY_FE: case SOCKETKEY_FE:
CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&FEKey), tmpSize); CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&FEKey), bodysize);
break; break;
default: { default: {
free(tmpBuf); free(fullpkt);
DEBUGLOG( DEBUGLOG(
std::cout << "[WARN]: UNSET KEYTYPE FOR SOCKET!! ABORTING SEND" << std::endl; std::cout << "[WARN]: UNSET KEYTYPE FOR SOCKET!! ABORTING SEND" << std::endl;
) )
@ -147,15 +149,11 @@ void CNSocket::sendPacket(void* buf, uint32_t type, size_t size) {
} }
} }
// send packet size
if (!sendData((uint8_t*)&tmpSize, sizeof(uint32_t)))
kill();
// send packet data! // send packet data!
if (alive && !sendData(tmpBuf, tmpSize)) if (alive && !sendData(fullpkt, bodysize+4))
kill(); kill();
free(tmpBuf); // free tmp buffer free(fullpkt);
} }
void CNSocket::setActiveKey(ACTIVEKEY key) { void CNSocket::setActiveKey(ACTIVEKEY key) {
@ -172,7 +170,7 @@ void CNSocket::step() {
// we got out packet size!!!! // we got out packet size!!!!
readSize = *((int32_t*)readBuffer); readSize = *((int32_t*)readBuffer);
// sanity check // sanity check
if (readSize > MAX_PACKETSIZE) { if (readSize > CN_PACKET_BUFFER_SIZE) {
kill(); kill();
return; return;
} }

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#define MAX_PACKETSIZE 8192
#define DEBUGLOG(x) if (settings::VERBOSITY) {x}; #define DEBUGLOG(x) if (settings::VERBOSITY) {x};
#include <iostream> #include <iostream>
@ -56,7 +55,8 @@
[4 bytes] - size of packet including the 4 byte packet type [4 bytes] - size of packet including the 4 byte packet type
[size bytes] - Encrypted packet (byte swapped && xor'd with 8 byte key; see CNSocketEncryption) [size bytes] - Encrypted packet (byte swapped && xor'd with 8 byte key; see CNSocketEncryption)
[4 bytes] - packet type (which is a combination of the first 4 bytes of the packet and a checksum in some versions) [4 bytes] - packet type (which is a combination of the first 4 bytes of the packet and a checksum in some versions)
[structure] [structure] - one member contains length of trailing data (expressed in packet-dependant structures)
[trailing data] - optional variable-length data that only some packets make use of
*/ */
// error checking calloc wrapper // error checking calloc wrapper
@ -71,6 +71,42 @@ inline void* xmalloc(size_t sz) {
return res; return res;
} }
// overflow-safe validation of variable-length packets
// for outbound packets
inline bool validOutVarPacket(size_t base, int32_t npayloads, size_t plsize) {
// check for multiplication overflow
if (npayloads > 0 && CN_PACKET_BUFFER_SIZE / (size_t)npayloads < plsize)
return false;
// it's safe to multiply
size_t trailing = npayloads * plsize;
// does it fit in a packet?
if (base + trailing > CN_PACKET_BUFFER_SIZE)
return false;
// everything is a-ok!
return true;
}
// for inbound packets
inline bool validInVarPacket(size_t base, int32_t npayloads, size_t plsize, size_t datasize) {
// check for multiplication overflow
if (npayloads > 0 && CN_PACKET_BUFFER_SIZE / (size_t)npayloads < plsize)
return false;
// it's safe to multiply
size_t trailing = npayloads * plsize;
// make sure size is exact
// datasize has already been validated against CN_PACKET_BUFFER_SIZE
if (datasize != base + trailing)
return false;
// everything is a-ok!
return true;
}
namespace CNSocketEncryption { namespace CNSocketEncryption {
// you won't believe how complicated they made it in the client :facepalm: // you won't believe how complicated they made it in the client :facepalm:
static constexpr const char* defaultKey = "m@rQn~W#"; static constexpr const char* defaultKey = "m@rQn~W#";
@ -104,7 +140,7 @@ private:
uint64_t EKey; uint64_t EKey;
uint64_t FEKey; uint64_t FEKey;
int32_t readSize = 0; int32_t readSize = 0;
uint8_t readBuffer[MAX_PACKETSIZE]; uint8_t readBuffer[CN_PACKET_BUFFER_SIZE];
int readBufferIndex = 0; int readBufferIndex = 0;
bool activelyReading = false; bool activelyReading = false;
bool alive = true; bool alive = true;

131
src/CombatManager.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "CombatManager.hpp"
#include "PlayerManager.hpp"
#include "NPCManager.hpp"
#include "ItemManager.hpp"
#include <assert.h>
void CombatManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_BEGIN, combatBegin);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_COMBAT_END, combatEnd);
REGISTER_SHARD_PACKET(P_CL2FE_DOT_DAMAGE_ONOFF, dotDamageOnOff);
}
void CombatManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
sP_CL2FE_REQ_PC_ATTACK_NPCs* pkt = (sP_CL2FE_REQ_PC_ATTACK_NPCs*)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// sanity check
if (!validInVarPacket(sizeof(sP_CL2FE_REQ_PC_ATTACK_NPCs), pkt->iNPCCnt, sizeof(int32_t), data->size)) {
std::cout << "[WARN] bad sP_CL2FE_REQ_PC_ATTACK_NPCs packet size\n";
return;
}
int32_t *pktdata = (int32_t*)((uint8_t*)data->buf + sizeof(sP_CL2FE_REQ_PC_ATTACK_NPCs));
/*
* Due to the possibility of multiplication overflow (and regular buffer overflow),
* both incoming and outgoing variable-length packets must be validated, at least if
* the number of trailing structs isn't well known (ie. it's from the client).
*/
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC), pkt->iNPCCnt, sizeof(sAttackResult))) {
std::cout << "[WARN] bad sP_FE2CL_PC_ATTACK_NPCs_SUCC packet size\n";
return;
}
// initialize response struct
size_t resplen = sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC) + pkt->iNPCCnt * sizeof(sAttackResult);
uint8_t respbuf[4096];
memset(respbuf, 0, resplen);
sP_FE2CL_PC_ATTACK_NPCs_SUCC *resp = (sP_FE2CL_PC_ATTACK_NPCs_SUCC*)respbuf;
sAttackResult *respdata = (sAttackResult*)(respbuf+sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC));
resp->iNPCCnt = pkt->iNPCCnt;
for (int i = 0; i < pkt->iNPCCnt; i++) {
if (NPCManager::NPCs.find(pktdata[i]) == NPCManager::NPCs.end()) {
// not sure how to best handle this
std::cout << "[WARN] pcAttackNpcs: mob ID not found" << std::endl;
return;
}
BaseNPC& mob = NPCManager::NPCs[pktdata[i]];
mob.appearanceData.iHP -= 100;
if (mob.appearanceData.iHP <= 0)
giveReward(sock);
// TODO: despawn mobs when they die
respdata[i].iID = mob.appearanceData.iNPC_ID;
respdata[i].iDamage = 100;
respdata[i].iHP = mob.appearanceData.iHP;
respdata[i].iHitFlag = 2;
}
sock->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_NPCs_SUCC, resplen);
// a bit of a hack: these are the same size, so we can reuse the output packet
assert(sizeof(sP_FE2CL_PC_ATTACK_NPCs_SUCC) == sizeof(sP_FE2CL_PC_ATTACK_NPCs));
sP_FE2CL_PC_ATTACK_NPCs *resp1 = (sP_FE2CL_PC_ATTACK_NPCs*)respbuf;
resp1->iPC_ID = plr->iID;
// send to other players
for (CNSocket *s : PlayerManager::players[sock].viewable) {
if (s == sock)
continue;
s->sendPacket((void*)respbuf, P_FE2CL_PC_ATTACK_NPCs, resplen);
}
}
void CombatManager::combatBegin(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::combatEnd(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {} // stub
void CombatManager::giveReward(CNSocket *sock) {
Player *plr = PlayerManager::getPlayer(sock);
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
assert(resplen < CN_PACKET_BUFFER_SIZE);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[resplen]; // not a variable length array, don't worry
sP_FE2CL_REP_REWARD_ITEM *reward = (sP_FE2CL_REP_REWARD_ITEM *)respbuf;
sItemReward *item = (sItemReward *)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
// don't forget to zero the buffer!
memset(respbuf, 0, resplen);
// update player
plr->money += 50;
plr->fusionmatter += 70;
// simple rewards
reward->m_iCandy = plr->money;
reward->m_iFusionMatter = plr->fusionmatter;
reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this
int slot = ItemManager::findFreeSlot(plr);
if (slot == -1) {
// no room for an item, but you still get FM and taros
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, sizeof(sP_FE2CL_REP_REWARD_ITEM));
} else {
// item reward
item->sItem.iType = 9;
item->sItem.iID = 1;
item->iSlotNum = slot;
item->eIL = 1; // Inventory Location. 1 means player inventory.
// update player
plr->Inven[slot] = item->sItem;
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
}
}

16
src/CombatManager.hpp Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "CNProtocol.hpp"
#include "CNShared.hpp"
#include "CNShardServer.hpp"
namespace CombatManager {
void init();
void pcAttackNpcs(CNSocket *sock, CNPacketData *data);
void combatBegin(CNSocket *sock, CNPacketData *data);
void combatEnd(CNSocket *sock, CNPacketData *data);
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
void giveReward(CNSocket *sock);
}

View File

@ -5,7 +5,7 @@
#define STRINGIFY(x) PacketMap(x, #x) #define STRINGIFY(x) PacketMap(x, #x)
/* /*
* Turns out there's not better way to do this... * Turns out there isn't better way to do this...
* We'll only support CL2* packets for now, since we only * We'll only support CL2* packets for now, since we only
* need to print those. * need to print those.
*/ */

View File

@ -4,6 +4,9 @@
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
#include "Player.hpp" #include "Player.hpp"
#include <string.h> // for memset() and memcmp()
#include <assert.h>
void ItemManager::init() { void ItemManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_DELETE, itemDeleteHandler);
@ -18,6 +21,7 @@ void ItemManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, itemTradeUnregisterItemHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_ITEM_UNREGISTER, itemTradeUnregisterItemHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, itemTradeRegisterCashHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_CASH_REGISTER, itemTradeRegisterCashHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, itemTradeChatHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TRADE_EMOTES_CHAT, itemTradeChatHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_CHEST_OPEN, chestOpenHandler);
} }
void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) { void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
@ -665,3 +669,63 @@ void ItemManager::itemTradeChatHandler(CNSocket* sock, CNPacketData* data) {
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT)); otherSock->sendPacket((void*)&resp, P_FE2CL_REP_PC_TRADE_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_TRADE_EMOTES_CHAT));
} }
void ItemManager::chestOpenHandler(CNSocket *sock, CNPacketData *data) {
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_CHEST_OPEN))
return; // ignore the malformed packet
sP_CL2FE_REQ_ITEM_CHEST_OPEN *pkt = (sP_CL2FE_REQ_ITEM_CHEST_OPEN *)data->buf;
Player *plr = PlayerManager::getPlayer(sock);
// item giving packet
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
assert(resplen < CN_PACKET_BUFFER_SIZE);
// we know it's only one trailing struct, so we can skip full validation
uint8_t respbuf[resplen]; // not a variable length array, don't worry
sP_FE2CL_REP_REWARD_ITEM *reward = (sP_FE2CL_REP_REWARD_ITEM *)respbuf;
sItemReward *item = (sItemReward *)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
// don't forget to zero the buffer!
memset(respbuf, 0, resplen);
// simple rewards
reward->iFatigue = 100; // prevents warning message
reward->iFatigue_Level = 1;
reward->iItemCnt = 1; // remember to update resplen if you change this
// item reward
item->sItem.iType = 0;
item->sItem.iID = 96;
item->iSlotNum = pkt->iSlotNum;
item->eIL = pkt->eIL;
// update player
plr->Inven[pkt->iSlotNum] = item->sItem;
// transmit item
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
// chest opening acknowledgement packet
INITSTRUCT(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, resp);
resp.iSlotNum = pkt->iSlotNum;
std::cout << "opening chest..." << std::endl;
sock->sendPacket((void*)&resp, P_FE2CL_REP_ITEM_CHEST_OPEN_SUCC, sizeof(sP_FE2CL_REP_ITEM_CHEST_OPEN_SUCC));
}
// TODO: use this in cleaned up ItemManager
int ItemManager::findFreeSlot(Player *plr) {
int i;
sItemBase free;
memset((void*)&free, 0, sizeof(sItemBase));
for (i = 0; i < AINVEN_COUNT; i++)
if (memcmp((void*)&plr->Inven[i], (void*)&free, sizeof(sItemBase)) == 0)
return i;
// not found
return -1;
}

View File

@ -1,9 +1,11 @@
#pragma once #pragma once
#include "CNShardServer.hpp" #include "CNShardServer.hpp"
#include "Player.hpp"
namespace ItemManager { namespace ItemManager {
void init(); void init();
void itemMoveHandler(CNSocket* sock, CNPacketData* data); void itemMoveHandler(CNSocket* sock, CNPacketData* data);
void itemDeleteHandler(CNSocket* sock, CNPacketData* data); void itemDeleteHandler(CNSocket* sock, CNPacketData* data);
void itemGMGiveHandler(CNSocket* sock, CNPacketData* data); void itemGMGiveHandler(CNSocket* sock, CNPacketData* data);
@ -17,4 +19,7 @@ namespace ItemManager {
void itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data); void itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data);
void itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data); void itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data);
void itemTradeChatHandler(CNSocket* sock, CNPacketData* data); void itemTradeChatHandler(CNSocket* sock, CNPacketData* data);
void chestOpenHandler(CNSocket* sock, CNPacketData* data);
int findFreeSlot(Player *plr);
} }

View File

@ -37,7 +37,7 @@ void NPCManager::init() {
// load temporary mob dump // load temporary mob dump
try { try {
std::ifstream inFile("mobs.json"); // not in settings, since it's temp std::ifstream inFile("data/mobs.json"); // not in settings, since it's temp
nlohmann::json npcData; nlohmann::json npcData;
// read file into json // read file into json

View File

@ -15,6 +15,7 @@ struct Player {
int HP; int HP;
int slot; // player slot, not nano slot int slot; // player slot, not nano slot
int32_t money; int32_t money;
int32_t fusionmatter;
sPCStyle PCStyle; sPCStyle PCStyle;
sPCStyle2 PCStyle2; sPCStyle2 PCStyle2;
sNano Nanos[37]; // acquired nanos sNano Nanos[37]; // acquired nanos

View File

@ -32,6 +32,7 @@ void PlayerManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, PlayerManager::setSpecialSwitchPlayer); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_SPECIAL_STATE_SWITCH, PlayerManager::setSpecialSwitchPlayer);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, PlayerManager::enterPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_ON, PlayerManager::enterPlayerVehicle);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, PlayerManager::exitPlayerVehicle); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VEHICLE_OFF, PlayerManager::exitPlayerVehicle);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_CHANGE_MENTOR, PlayerManager::changePlayerGuide);
} }
void PlayerManager::addPlayer(CNSocket* key, Player plr) { void PlayerManager::addPlayer(CNSocket* key, Player plr) {
@ -651,6 +652,21 @@ void PlayerManager::setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data) {
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, sizeof(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC)); sock->sendPacket((void*)&response, P_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC, sizeof(sP_FE2CL_REP_PC_SPECIAL_STATE_SWITCH_SUCC));
} }
void PlayerManager::changePlayerGuide(CNSocket *sock, CNPacketData *data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_CHANGE_MENTOR))
return;
sP_CL2FE_REQ_PC_CHANGE_MENTOR *pkt = (sP_CL2FE_REQ_PC_CHANGE_MENTOR*)data->buf;
INITSTRUCT(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, resp);
Player *plr = getPlayer(sock);
resp.iMentor = pkt->iMentor;
resp.iMentorCnt = 1;
resp.iFusionMatter = plr->fusionmatter; // no cost
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_CHANGE_MENTOR_SUCC, sizeof(sP_FE2CL_REP_PC_CHANGE_MENTOR_SUCC));
}
#pragma region Helper methods #pragma region Helper methods
Player *PlayerManager::getPlayer(CNSocket* key) { Player *PlayerManager::getPlayer(CNSocket* key) {
return players[key].plr; return players[key].plr;

View File

@ -45,6 +45,7 @@ namespace PlayerManager {
void exitGame(CNSocket* sock, CNPacketData* data); void exitGame(CNSocket* sock, CNPacketData* data);
void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data); void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data);
void changePlayerGuide(CNSocket *sock, CNPacketData *data);
void enterPlayerVehicle(CNSocket* sock, CNPacketData* data); void enterPlayerVehicle(CNSocket* sock, CNPacketData* data);
void exitPlayerVehicle(CNSocket* sock, CNPacketData* data); void exitPlayerVehicle(CNSocket* sock, CNPacketData* data);

View File

@ -2,6 +2,7 @@
#include "CNShardServer.hpp" #include "CNShardServer.hpp"
#include "PlayerManager.hpp" #include "PlayerManager.hpp"
#include "ChatManager.hpp" #include "ChatManager.hpp"
#include "CombatManager.hpp"
#include "ItemManager.hpp" #include "ItemManager.hpp"
#include "MissionManager.hpp" #include "MissionManager.hpp"
#include "NanoManager.hpp" #include "NanoManager.hpp"
@ -17,10 +18,21 @@
#endif #endif
#include <string> #include <string>
CNShardServer *shardServer;
std::thread *shardThread;
void startShard(CNShardServer* server) { void startShard(CNShardServer* server) {
server->start(); server->start();
} }
// terminate gracefully on SIGINT (for gprof)
void terminate(int arg) {
std::cout << "OpenFusion: terminating" << std::endl;
shardServer->kill();
shardThread->join();
exit(0);
}
int main() { int main() {
#ifdef _WIN32 #ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;
@ -31,12 +43,14 @@ int main() {
#else #else
// tell the OS to not kill us if you use a broken pipe, just let us know thru recv() or send() // tell the OS to not kill us if you use a broken pipe, just let us know thru recv() or send()
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
signal(SIGINT, terminate); // TODO: use sigaction() instead
#endif #endif
settings::init(); settings::init();
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl; std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
std::cout << "[INFO] Intializing Packet Managers..." << std::endl; std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
PlayerManager::init(); PlayerManager::init();
ChatManager::init(); ChatManager::init();
CombatManager::init();
ItemManager::init(); ItemManager::init();
MissionManager::init(); MissionManager::init();
NanoManager::init(); NanoManager::init();
@ -46,14 +60,14 @@ int main() {
std::cout << "[INFO] Starting Server Threads..." << std::endl; std::cout << "[INFO] Starting Server Threads..." << std::endl;
CNLoginServer loginServer(settings::LOGINPORT); CNLoginServer loginServer(settings::LOGINPORT);
CNShardServer shardServer(settings::SHARDPORT); shardServer = new CNShardServer(settings::SHARDPORT);
std::thread shardThread(startShard, (CNShardServer*)&shardServer); shardThread = new std::thread(startShard, (CNShardServer*)shardServer);
loginServer.start(); loginServer.start();
shardServer.kill(); shardServer->kill();
shardThread.join(); shardThread->join();
#ifdef _WIN32 #ifdef _WIN32
WSACleanup(); WSACleanup();

View File

@ -18,8 +18,8 @@ int settings::SPAWN_X = 179213;
int settings::SPAWN_Y = 268451; int settings::SPAWN_Y = 268451;
int settings::SPAWN_Z = -4210; int settings::SPAWN_Z = -4210;
std::string settings::GMPASS = "pass"; std::string settings::GMPASS = "pass";
std::string settings::NPCJSON = "NPCs.json"; std::string settings::NPCJSON = "data/NPCs.json";
std::string settings::WARPJSON = "warps.json"; std::string settings::WARPJSON = "data/warps.json";
std::string settings::MOTDSTRING = "Welcome to OpenFusion!"; std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
bool settings::GM = false; bool settings::GM = false;