mirror of
				https://github.com/OpenFusionProject/OpenFusion.git
				synced 2025-11-04 02:30:21 +00:00 
			
		
		
		
	Merge pull request #55 from dongresource/combat1
Implemented combat, drops, crates and the guide changer
This commit is contained in:
		
							
								
								
									
										6
									
								
								.vimrc
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								.vimrc
									
									
									
									
									
								
							@@ -1,9 +1,9 @@
 | 
			
		||||
" 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
 | 
			
		||||
" start vim in this dir, or you can just load it directly with ':so .vimrc'
 | 
			
		||||
" every time.
 | 
			
		||||
" start vim in this dir. Alternatively you can just load it directly with
 | 
			
		||||
" ':so .vimrc' every time.
 | 
			
		||||
set tabstop=4
 | 
			
		||||
set shiftwidth=4
 | 
			
		||||
set expandtab
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
CC=clang
 | 
			
		||||
CXX=clang++
 | 
			
		||||
# -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
 | 
			
		||||
LDFLAGS=-lpthread -ldl
 | 
			
		||||
# specifies the name of our exectuable
 | 
			
		||||
@@ -14,22 +14,23 @@ PROTOCOL_VERSION?=104
 | 
			
		||||
# Windows-specific
 | 
			
		||||
WIN_CC=x86_64-w64-mingw32-gcc
 | 
			
		||||
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_LDFLAGS=-static -lws2_32 -lwsock32
 | 
			
		||||
WIN_SERVER=bin/winfusion.exe
 | 
			
		||||
 | 
			
		||||
CC_SRC=\
 | 
			
		||||
CSRC=\
 | 
			
		||||
	src/contrib/bcrypt/bcrypt.c\
 | 
			
		||||
	src/contrib/bcrypt/crypt_blowfish.c\
 | 
			
		||||
	src/contrib/bcrypt/crypt_gensalt.c\
 | 
			
		||||
	src/contrib/bcrypt/wrapper.c\
 | 
			
		||||
	src/contrib/sqlite/sqlite3.c\
 | 
			
		||||
 | 
			
		||||
CXX_SRC=\
 | 
			
		||||
CXXSRC=\
 | 
			
		||||
	src/contrib/sqlite/sqlite3pp.cpp\
 | 
			
		||||
	src/contrib/sqlite/sqlite3ppext.cpp\
 | 
			
		||||
	src/ChatManager.cpp\
 | 
			
		||||
	src/CombatManager.cpp\
 | 
			
		||||
	src/CNLoginServer.cpp\
 | 
			
		||||
	src/CNProtocol.cpp\
 | 
			
		||||
	src/CNShardServer.cpp\
 | 
			
		||||
@@ -47,7 +48,7 @@ CXX_SRC=\
 | 
			
		||||
	src/settings.cpp\
 | 
			
		||||
 | 
			
		||||
# headers (for timestamp purposes)
 | 
			
		||||
CC_HDR=\
 | 
			
		||||
CHDR=\
 | 
			
		||||
	src/contrib/bcrypt/bcrypt.h\
 | 
			
		||||
	src/contrib/bcrypt/crypt_blowfish.h\
 | 
			
		||||
	src/contrib/bcrypt/crypt_gensalt.h\
 | 
			
		||||
@@ -56,13 +57,14 @@ CC_HDR=\
 | 
			
		||||
	src/contrib/sqlite/sqlite3.h\
 | 
			
		||||
	src/contrib/sqlite/sqlite3ext.h\
 | 
			
		||||
 | 
			
		||||
CXX_HDR=\
 | 
			
		||||
CXXHDR=\
 | 
			
		||||
	src/contrib/bcrypt/BCrypt.hpp\
 | 
			
		||||
	src/contrib/sqlite/sqlite3pp.h\
 | 
			
		||||
	src/contrib/sqlite/sqlite3ppext.h\
 | 
			
		||||
	src/contrib/INIReader.hpp\
 | 
			
		||||
	src/contrib/JSON.hpp\
 | 
			
		||||
	src/ChatManager.hpp\
 | 
			
		||||
	src/CombatManager.hpp\
 | 
			
		||||
	src/CNLoginServer.hpp\
 | 
			
		||||
	src/CNProtocol.hpp\
 | 
			
		||||
	src/CNShardServer.hpp\
 | 
			
		||||
@@ -80,10 +82,10 @@ CXX_HDR=\
 | 
			
		||||
	src/PlayerManager.hpp\
 | 
			
		||||
	src/settings.hpp\
 | 
			
		||||
 | 
			
		||||
CC_OBJ=$(CC_SRC:.c=.o)
 | 
			
		||||
CXX_OBJ=$(CXX_SRC:.cpp=.o)
 | 
			
		||||
COBJ=$(CSRC:.c=.o)
 | 
			
		||||
CXXOBJ=$(CXXSRC:.cpp=.o)
 | 
			
		||||
 | 
			
		||||
OBJ=$(CC_OBJ) $(CXX_OBJ)
 | 
			
		||||
OBJ=$(COBJ) $(CXXOBJ)
 | 
			
		||||
 | 
			
		||||
all: $(SERVER)
 | 
			
		||||
 | 
			
		||||
@@ -92,22 +94,24 @@ windows: $(SERVER)
 | 
			
		||||
# assign Windows-specific values if targeting Windows
 | 
			
		||||
windows : CC=$(WIN_CC)
 | 
			
		||||
windows : CXX=$(WIN_CXX)
 | 
			
		||||
windows : CCFLAGS=$(WIN_CCFLAGS)
 | 
			
		||||
windows : CFLAGS=$(WIN_CFLAGS)
 | 
			
		||||
windows : CXXFLAGS=$(WIN_CXXFLAGS)
 | 
			
		||||
windows : LDFLAGS=$(WIN_LDFLAGS)
 | 
			
		||||
windows : SERVER=$(WIN_SERVER)
 | 
			
		||||
 | 
			
		||||
$(CC_OBJ): %.o: %.c $(CC_HDR)
 | 
			
		||||
	$(CC) -c $(CCFLAGS) -o $@ $<
 | 
			
		||||
.SUFFIX: .o .c .cpp .hpp
 | 
			
		||||
 | 
			
		||||
$(CXX_OBJ): %.o: %.cpp $(CXX_HDR)
 | 
			
		||||
.c.o: $(CHDR)
 | 
			
		||||
	$(CC) -c $(CFLAGS) -o $@ $<
 | 
			
		||||
 | 
			
		||||
.cpp.o: $(CXXHDR)
 | 
			
		||||
	$(CXX) -c $(CXXFLAGS) -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(SERVER): $(OBJ) $(CC_HDR) $(CXX_HDR)
 | 
			
		||||
$(SERVER): $(OBJ) $(CHDR) $(CXXHDR)
 | 
			
		||||
	mkdir -p bin
 | 
			
		||||
	$(CXX) $(OBJ) $(LDFLAGS) -o $(SERVER)
 | 
			
		||||
 | 
			
		||||
.PHONY: all windows clean
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f $(OBJ) $(SERVER) $(WIN_SERVER)
 | 
			
		||||
	rm -f $(OBJ) $(SERVER) $(WIN_SERVER)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							@@ -16,7 +16,7 @@ tl;dr:
 | 
			
		||||
 | 
			
		||||
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`
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
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.).
 | 
			
		||||
 | 
			
		||||
### Item commands
 | 
			
		||||
* /itemN [type] [itemId] [amount]
 | 
			
		||||
* `/itemN [type] [itemId] [amount]`
 | 
			
		||||
  (Refer to the [item list](https://docs.google.com/spreadsheets/d/1mpoJ9iTHl_xLI4wQ_9UvIDYNcsDYscdkyaGizs43TCg/))
 | 
			
		||||
 | 
			
		||||
### Nano commands
 | 
			
		||||
* /nano [id] (1-36)
 | 
			
		||||
* /nano_equip [id] (1-36) [slot] (0-2)
 | 
			
		||||
* /nano_unequip [slot] (0-2)
 | 
			
		||||
* /nano_active [slot] (0-2)
 | 
			
		||||
* `/nano [id] (1-36)`
 | 
			
		||||
* `/nano_equip [id] (1-36) [slot] (0-2)`
 | 
			
		||||
* `/nano_unequip [slot] (0-2)`
 | 
			
		||||
* `/nano_active [slot] (0-2)`
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@ npcdistance=16000
 | 
			
		||||
# little message players see when they enter the game
 | 
			
		||||
motd=Welcome to OpenFusion!
 | 
			
		||||
# NPC json data
 | 
			
		||||
npcdata=NPCs.json
 | 
			
		||||
npcdata=data/NPCs.json
 | 
			
		||||
# warp target json data
 | 
			
		||||
warpdata=warps.json
 | 
			
		||||
warpdata=data/warps.json
 | 
			
		||||
# is everyone a GM?
 | 
			
		||||
gm=true
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -123,23 +123,25 @@ void CNSocket::sendPacket(void* buf, uint32_t type, size_t size) {
 | 
			
		||||
    if (!alive)
 | 
			
		||||
        return;
 | 
			
		||||
    
 | 
			
		||||
    int tmpSize = size + sizeof(uint32_t);
 | 
			
		||||
    uint8_t* tmpBuf = (uint8_t*)xmalloc(tmpSize);
 | 
			
		||||
    size_t bodysize = size + sizeof(uint32_t);
 | 
			
		||||
    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
 | 
			
		||||
    memcpy(tmpBuf, (void*)&type, sizeof(uint32_t));
 | 
			
		||||
    memcpy(tmpBuf+sizeof(uint32_t), buf, size);
 | 
			
		||||
    memcpy(body, (void*)&type, sizeof(uint32_t));
 | 
			
		||||
    memcpy(body+sizeof(uint32_t), buf, size);
 | 
			
		||||
 | 
			
		||||
    // encrypt the packet
 | 
			
		||||
    switch (activeKey) {
 | 
			
		||||
        case SOCKETKEY_E:
 | 
			
		||||
            CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&EKey), tmpSize);
 | 
			
		||||
            CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&EKey), bodysize);
 | 
			
		||||
            break;
 | 
			
		||||
        case SOCKETKEY_FE:
 | 
			
		||||
            CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&FEKey), tmpSize);
 | 
			
		||||
            CNSocketEncryption::encryptData((uint8_t*)body, (uint8_t*)(&FEKey), bodysize);
 | 
			
		||||
            break;
 | 
			
		||||
        default: {
 | 
			
		||||
            free(tmpBuf);
 | 
			
		||||
            free(fullpkt);
 | 
			
		||||
            DEBUGLOG(
 | 
			
		||||
                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!
 | 
			
		||||
    if (alive && !sendData(tmpBuf, tmpSize)) 
 | 
			
		||||
    if (alive && !sendData(fullpkt, bodysize+4)) 
 | 
			
		||||
        kill();
 | 
			
		||||
 | 
			
		||||
    free(tmpBuf); // free tmp buffer
 | 
			
		||||
    free(fullpkt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CNSocket::setActiveKey(ACTIVEKEY key) {
 | 
			
		||||
@@ -172,7 +170,7 @@ void CNSocket::step() {
 | 
			
		||||
            // we got out packet size!!!!
 | 
			
		||||
            readSize = *((int32_t*)readBuffer);
 | 
			
		||||
            // sanity check
 | 
			
		||||
            if (readSize > MAX_PACKETSIZE) {
 | 
			
		||||
            if (readSize > CN_PACKET_BUFFER_SIZE) {
 | 
			
		||||
                kill();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define MAX_PACKETSIZE 8192
 | 
			
		||||
#define DEBUGLOG(x) if (settings::VERBOSITY) {x};
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -56,7 +55,8 @@
 | 
			
		||||
        [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)
 | 
			
		||||
            [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
 | 
			
		||||
@@ -71,6 +71,42 @@ inline void* xmalloc(size_t sz) {
 | 
			
		||||
    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 {
 | 
			
		||||
    // you won't believe how complicated they made it in the client :facepalm:
 | 
			
		||||
    static constexpr const char* defaultKey = "m@rQn~W#";
 | 
			
		||||
@@ -104,7 +140,7 @@ private:
 | 
			
		||||
    uint64_t EKey;
 | 
			
		||||
    uint64_t FEKey;
 | 
			
		||||
    int32_t readSize = 0;
 | 
			
		||||
    uint8_t readBuffer[MAX_PACKETSIZE];
 | 
			
		||||
    uint8_t readBuffer[CN_PACKET_BUFFER_SIZE];
 | 
			
		||||
    int readBufferIndex = 0;
 | 
			
		||||
    bool activelyReading = false;
 | 
			
		||||
    bool alive = true;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								src/CombatManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/CombatManager.cpp
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										16
									
								
								src/CombatManager.hpp
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
#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
 | 
			
		||||
 * need to print those.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,9 @@
 | 
			
		||||
#include "PlayerManager.hpp"
 | 
			
		||||
#include "Player.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string.h> // for memset() and memcmp()
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
void ItemManager::init() {
 | 
			
		||||
    REGISTER_SHARD_PACKET(P_CL2FE_REQ_ITEM_MOVE, itemMoveHandler);
 | 
			
		||||
    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_CASH_REGISTER, itemTradeRegisterCashHandler);
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -664,4 +668,64 @@ 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));
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "CNShardServer.hpp"
 | 
			
		||||
#include "Player.hpp"
 | 
			
		||||
 | 
			
		||||
namespace ItemManager {
 | 
			
		||||
    void init();	
 | 
			
		||||
 | 
			
		||||
    void itemMoveHandler(CNSocket* sock, CNPacketData* data);   
 | 
			
		||||
    void itemDeleteHandler(CNSocket* sock, CNPacketData* data);   
 | 
			
		||||
    void itemGMGiveHandler(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
@@ -17,4 +19,7 @@ namespace ItemManager {
 | 
			
		||||
    void itemTradeUnregisterItemHandler(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
    void itemTradeRegisterCashHandler(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
    void itemTradeChatHandler(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
    void chestOpenHandler(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
 | 
			
		||||
    int findFreeSlot(Player *plr);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ void NPCManager::init() {
 | 
			
		||||
 | 
			
		||||
    // load temporary mob dump
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
        // read file into json
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ struct Player {
 | 
			
		||||
    int HP;
 | 
			
		||||
    int slot; // player slot, not nano slot
 | 
			
		||||
    int32_t money;
 | 
			
		||||
    int32_t fusionmatter;
 | 
			
		||||
    sPCStyle PCStyle;
 | 
			
		||||
    sPCStyle2 PCStyle2;
 | 
			
		||||
    sNano Nanos[37]; // acquired nanos
 | 
			
		||||
 
 | 
			
		||||
@@ -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_VEHICLE_ON, PlayerManager::enterPlayerVehicle);
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
Player *PlayerManager::getPlayer(CNSocket* key) {
 | 
			
		||||
    return players[key].plr;
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,7 @@ namespace PlayerManager {
 | 
			
		||||
    void exitGame(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
 | 
			
		||||
    void setSpecialSwitchPlayer(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
    void changePlayerGuide(CNSocket *sock, CNPacketData *data);
 | 
			
		||||
 | 
			
		||||
    void enterPlayerVehicle(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
    void exitPlayerVehicle(CNSocket* sock, CNPacketData* data);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/main.cpp
									
									
									
									
									
								
							@@ -2,6 +2,7 @@
 | 
			
		||||
#include "CNShardServer.hpp"
 | 
			
		||||
#include "PlayerManager.hpp"
 | 
			
		||||
#include "ChatManager.hpp"
 | 
			
		||||
#include "CombatManager.hpp"
 | 
			
		||||
#include "ItemManager.hpp"
 | 
			
		||||
#include "MissionManager.hpp"
 | 
			
		||||
#include "NanoManager.hpp"
 | 
			
		||||
@@ -17,10 +18,21 @@
 | 
			
		||||
#endif
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
CNShardServer *shardServer;
 | 
			
		||||
std::thread *shardThread;
 | 
			
		||||
 | 
			
		||||
void startShard(CNShardServer* server) {
 | 
			
		||||
    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() {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    WSADATA wsaData;
 | 
			
		||||
@@ -31,12 +43,14 @@ int main() {
 | 
			
		||||
#else
 | 
			
		||||
    // 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(SIGINT, terminate); // TODO: use sigaction() instead
 | 
			
		||||
#endif
 | 
			
		||||
    settings::init();
 | 
			
		||||
    std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
 | 
			
		||||
    std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
 | 
			
		||||
    PlayerManager::init();
 | 
			
		||||
    ChatManager::init();
 | 
			
		||||
    CombatManager::init();
 | 
			
		||||
    ItemManager::init();
 | 
			
		||||
    MissionManager::init();
 | 
			
		||||
    NanoManager::init();
 | 
			
		||||
@@ -46,14 +60,14 @@ int main() {
 | 
			
		||||
 | 
			
		||||
    std::cout << "[INFO] Starting Server Threads..." << std::endl;
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
    shardServer.kill();
 | 
			
		||||
    shardThread.join();
 | 
			
		||||
    shardServer->kill();
 | 
			
		||||
    shardThread->join();
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    WSACleanup();
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ int settings::SPAWN_X = 179213;
 | 
			
		||||
int settings::SPAWN_Y = 268451;
 | 
			
		||||
int settings::SPAWN_Z = -4210;
 | 
			
		||||
std::string settings::GMPASS = "pass";
 | 
			
		||||
std::string settings::NPCJSON = "NPCs.json";
 | 
			
		||||
std::string settings::WARPJSON = "warps.json";
 | 
			
		||||
std::string settings::NPCJSON = "data/NPCs.json";
 | 
			
		||||
std::string settings::WARPJSON = "data/warps.json";
 | 
			
		||||
std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
 | 
			
		||||
bool settings::GM = false;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user