Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Onii-chan
2020-08-22 13:35:43 +02:00
27 changed files with 25969 additions and 40 deletions

6
.gitignore vendored
View File

@@ -1,7 +1,11 @@
.vscode
.vscode/
bin/*
notes.txt
config.ini
*.o
tags
*~
CMakeFiles/
CMakeCache.txt
build/
.vs/

48
CMakeLists.txt Normal file
View File

@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.13)
project(OpenFusion)
set(CMAKE_CXX_STANDARD 17)
# OpenFusion supports multiple packet/struct versions
# 104 is the default version to build which can be changed
# For example: cmake -B build -DPROTOCOL_VERSION=728
set(PROTOCOL_VERSION 104 CACHE STRING "The packet version to build")
add_compile_definitions(PROTOCOL_VERSION=${PROTOCOL_VERSION})
# Disallow in-source builds
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please refer to the wiki for more information. Please remove the CMakeFiles folder and the CMakeCache.txt file.")
endif()
# Output binaries to the bin folder in the source directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
# Put CMake targets (ALL_BUILD/ZERO_CHECK) into a folder
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# Set the OpenFusion project as the default startup project for VS
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT openfusion)
if (WIN32)
# Set the output binary name to winfusion to match the regular Makefile
set(BIN_NAME winfusion)
else()
set(BIN_NAME fusion)
endif()
include_directories(src)
file(GLOB_RECURSE SOURCES src/**.cpp src/**.hpp)
add_executable(openfusion ${SOURCES})
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
# Use pthreads if not generating a VS solution or MinGW makefile (because MinGW will prefer Win32 threads)
# Checking if the compiler ID is MSVC will allow us to open the project as a CMake project in VS.
# It's not something you should do, but it's there if you need it...
if (NOT CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND NOT CMAKE_GENERATOR MATCHES "MinGW Makefiles")
find_package(Threads REQUIRED)
target_link_libraries(openfusion pthread)
endif()

View File

@@ -1,9 +1,21 @@
The OpenFusion MIT except Malorn License
MIT License
Copyright 2020 Seth Stubbs
Copyright (c) 2020 Seth Stubbs
Excluding the individual known as "MalornWS" and their associates, permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,13 +1,17 @@
CXX=clang++
# -w suppresses all warnings (the part that's commented out helps me find memory leaks, it ruins performance though!)
CXXFLAGS=-std=c++17 -O3 #-g3 -fsanitize=address
CXXFLAGS=-std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
LDFLAGS=-lpthread
# specifies the name of our exectuable
SERVER=bin/fusion
# assign protocol version
# this can be overriden by ex. make PROTOCOL_VERSION=728
PROTOCOL_VERSION?=104
# Windows-specific
WIN_CXX=x86_64-w64-mingw32-g++
WIN_CXXFLAGS=-std=c++17 -O3 #-g3 -fsanitize=address
WIN_CXXFLAGS=-std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) #-g3 -fsanitize=address
WIN_LDFLAGS=-static -lws2_32 -lwsock32
WIN_SERVER=bin/winfusion.exe
@@ -21,6 +25,7 @@ SRC=\
src/CNStructs.cpp\
src/main.cpp\
src/NanoManager.cpp\
src/ItemManager.cpp\
src/NPCManager.cpp\
src/Player.cpp\
src/PlayerManager.cpp\
@@ -34,8 +39,10 @@ HDR=\
src/CNShardServer.hpp\
src/CNShared.hpp\
src/CNStructs.hpp\
src/INIReader.hpp\
src/contrib/INIReader.hpp\
src/contrib/JSON.hpp\
src/NanoManager.hpp\
src/ItemManager.hpp\
src/NPCManager.hpp\
src/Player.hpp\
src/PlayerManager.hpp\
@@ -47,7 +54,7 @@ all: $(SERVER)
windows: $(SERVER)
# Assign Windows-specific values if targeting Windows
# assign Windows-specific values if targeting Windows
windows : CXX=$(WIN_CXX)
windows : CXXFLAGS=$(WIN_CXXFLAGS)
windows : LDFLAGS=$(WIN_LDFLAGS)

1
NPCs.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,8 @@
![](res/radiorave_logo.png)
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/OpenFusionProject/OpenFusion?svg=true)](https://ci.appveyor.com/project/Raymonf/openfusion)
[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?logo=discord)](https://discord.gg/DYavckB)
OpenFusion is a landwalker server for FusionFall. It currently supports versions `beta-20100104` and `beta-20100728` of the original game.
Further documentation pending.
@@ -21,6 +24,8 @@ Currently the client by default connects to a public server hosted by Cake. Chan
You have two randomized characters available to you on the Character Selection screen, one boy, one girl.
You can also make your own character and play through the tutorial. The tutorial can be skipped by pressing the ~ key.
If you want, [compiled binaries (artifacts) for each new commit can be found on AppVeyor.](https://ci.appveyor.com/project/Raymonf/openfusion)
For a more detailed overview of the game's architecture and how to configure it, read the following sections.
## Architecture
@@ -72,7 +77,15 @@ Some modern IDEs/text editors do this automatically. If all else fails, use Note
## Compiling
OpenFusion can be compiled from source using the included makefile. A detailed compilation guide is available for Windows users [in the wiki](https://github.com/OpenFusionProject/OpenFusion/wiki/Compilation-on-Windows). Otherwise, to compile it for the current platform you're on, just run `make` with the correct build tools installed (currently make and clang).
You have two choices for compiling OpenFusion: the included Makefile and the included CMakeLists file.
### Makefile
A detailed compilation guide is available for Windows users in the wiki [using MinGW-w64 and MSYS2](https://github.com/OpenFusionProject/OpenFusion/wiki/Compilation-on-Windows). Otherwise, to compile it for the current platform you're on, just run `make` with the correct build tools installed (currently make and clang).
### CMake
A detailed guide is available [in the wiki](https://github.com/OpenFusionProject/OpenFusion/wiki/Compilation-with-CMake-or-Visual-Studio) for people using regular old CMake or the version of CMake that comes with Visual Studio. tl;dr: `cmake -B build`
## "Gameplay"

79
appveyor.yml Normal file
View File

@@ -0,0 +1,79 @@
version: 'openfusion-{branch}-{build}'
image:
- Visual Studio 2019
- Ubuntu2004
platform:
- x64
configuration:
- Release
for:
-
matrix:
only:
- image: Ubuntu2004
build_script:
- ps: |
$versions = "104", "728"
foreach ($version in $versions) {
Write-Output "Cleaning old output"
Invoke-Expression "make clean"
if ($LASTEXITCODE -ne "0") {
Write-Error "make clean failed for version $version" -ErrorAction Stop
}
Write-Output "Building version $version"
Invoke-Expression "make PROTOCOL_VERSION=$version"
if ($LASTEXITCODE -ne "0") {
Write-Error "make failed for version $version" -ErrorAction Stop
}
Rename-Item -Path "bin/fusion" -newName "$version-fusion"
Write-Output "Built version $version"
}
artifacts:
- path: bin
name: ubuntu20_04-bin-x64
type: zip
-
matrix:
only:
- image: Visual Studio 2019
build_script:
- ps: |
$versions = "104", "728"
$configurations = "Release", "Debug"
# AppVeyor uses VS2019 Community
$vsPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community"
Import-Module "$vsPath\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation
foreach ($version in $versions) {
if (Test-Path -LiteralPath "build") {
Remove-Item "build" -Recurse
Write-Output "Deleted existing build folder"
}
Invoke-Expression "cmake -B build -DPROTOCOL_VERSION=$version"
if ($LASTEXITCODE -ne "0") {
Write-Error "cmake generation failed for version $version" -ErrorAction Stop
}
Write-Output "Generated build files for version $version"
foreach ($configuration in $configurations) {
Write-Output "Building version $version $configuration"
Invoke-Expression "msbuild build\OpenFusion.sln /p:Configuration=$configuration"
if ($LASTEXITCODE -ne "0") {
Write-Error "msbuild build failed for version $version" -ErrorAction Stop
}
Rename-Item -Path "bin/$configuration" -newName "$version-$configuration"
Write-Output "Built version $version $configuration"
}
}
artifacts:
- path: bin
name: windows-vs2019-bin-x64
type: zip

View File

@@ -4,17 +4,19 @@
port=8001
# enables two randomly generated characters in the
# character selection menu for convenience
randomcharacters=false
randomcharacters=true
# Shard Server configuration
[shard]
port=8002
# you'll want to change this one
ip=192.168.1.183
ip=127.0.0.1
# distance at which other players and NPCs become visible
view=20000
# little message players see when they enter the game
motd=Welcome to OpenFusion!
# NPC json data
npcdata=NPCs.json
# spawn coordinates (Z is height)
# the supplied defaults are at City Hall

View File

@@ -91,15 +91,23 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
loginSessions[sock].characters[UID].z = charInfo->iZ;
loginSessions[sock].characters[UID].PCStyle = charInfo->sPC_Style;
loginSessions[sock].characters[UID].PCStyle2 = charInfo->sPC_Style2;
loginSessions[sock].characters[UID].IsGM = false;
for (int i = 0; i < AEQUIP_COUNT; i++) {
// setup item
// setup equips
charInfo->aEquip[i].iID = 0;
charInfo->aEquip[i].iType = i;
charInfo->aEquip[i].iOpt = 0;
loginSessions[sock].characters[UID].Equip[i] = charInfo->aEquip[i];
}
for (int i = 0; i < AINVEN_COUNT; i++) {
// setup inventories
loginSessions[sock].characters[UID].Inven[i].iID = 0;
loginSessions[sock].characters[UID].Inven[i].iType = 0;
loginSessions[sock].characters[UID].Inven[i].iOpt = 0;
}
// set default to the first character
if (i == 0)
loginSessions[sock].selectedChar = UID;
@@ -181,6 +189,18 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
std::cout << "\tiEquipLBID: " << (int)character->sOn_Item.iEquipLBID << std::endl;
std::cout << "\tiEquipFootID: " << (int)character->sOn_Item.iEquipFootID << std::endl;
)
int64_t UID = character->PCStyle.iPC_UID;
// commented and disabled for now
//bool BecomeGM;
//if (U16toU8(character->PCStyle.szFirstName) == settings::GMPASS) {
// BecomeGM = true;
// U8toU16("GM",character->PCStyle.szFirstName);
//} else {
// BecomeGM = false;
//}
character->PCStyle.iNameCheck = 1;
response->sPC_Style = character->PCStyle;
@@ -190,7 +210,6 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
response->iLevel = 1;
response->sOn_Item = character->sOn_Item;
int64_t UID = character->PCStyle.iPC_UID;
loginSessions[sock].characters[UID] = Player();
loginSessions[sock].characters[UID].level = 1;
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
@@ -207,6 +226,7 @@ void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
loginSessions[sock].characters[UID].Equip[2].iType = 2;
loginSessions[sock].characters[UID].Equip[3].iID = character->sOn_Item.iEquipFootID; // foot!
loginSessions[sock].characters[UID].Equip[3].iType = 3;
loginSessions[sock].characters[UID].IsGM = false;
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC), sock->getEKey()));
break;

View File

@@ -73,11 +73,18 @@ CNSocket::CNSocket(SOCKET s, PacketHandler ph): sock(s), pHandler(ph) {
bool CNSocket::sendData(uint8_t* data, int size) {
int sentBytes = 0;
int maxTries = 10;
while (sentBytes < size) {
int sent = send(sock, (buffer_t*)(data + sentBytes), size - sentBytes, 0); // no flags defined
if (SOCKETERROR(sent))
if (SOCKETERROR(sent)) {
if (errno == 11 && maxTries > 0) {
maxTries--;
continue; // try again
}
std::cout << "[FATAL] SOCKET ERROR: " << errno << std::endl;
return false; // error occured while sending bytes
}
sentBytes += sent;
}
@@ -116,6 +123,11 @@ void CNSocket::kill() {
}
void CNSocket::sendPacket(CNPacketData* pak) {
if (!alive) {
delete pak;
return;
}
int tmpSize = pak->size + sizeof(uint32_t);
uint8_t* tmpBuf = (uint8_t*)xmalloc(tmpSize);
@@ -127,10 +139,12 @@ void CNSocket::sendPacket(CNPacketData* pak) {
CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&pak->key), tmpSize);
// send packet size
sendData((uint8_t*)&tmpSize, sizeof(uint32_t));
if (!sendData((uint8_t*)&tmpSize, sizeof(uint32_t)))
kill();
// send packet data!
sendData(tmpBuf, tmpSize);
if (alive && !sendData(tmpBuf, tmpSize))
kill();
delete pak;
free(tmpBuf); // free tmp buffer

View File

@@ -1,8 +1,12 @@
#include "CNStructs.hpp"
std::string U16toU8(char16_t* src) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
return convert.to_bytes(src);
try {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
return convert.to_bytes(src);
} catch(std::exception e) {
return "";
}
}
// returns number of char16_t that was written at des

View File

@@ -1,23 +1,29 @@
/*
/*
CNStructs.hpp - defines some basic structs & useful methods for packets used by FusionFall based on the version defined
*/
#ifndef _CNS_HPP
#define _CNS_HPP
#ifdef _MSC_VER
// codecvt_* is deprecated in C++17 and MSVC will throw an annoying warning because of that.
// Defining this before anything else to silence it.
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
#endif
#include <iostream>
#include <stdio.h>
#include <stdint.h>
// Can't use this in MSVC.
#ifndef _MSC_VER
#include <sys/time.h>
#include <sys/time.h>
#else
#include <time.h>
// Can't use this in MSVC.
#include <time.h>
#endif
#include <cstring>
#include <string>
#include <locale>
#include <codecvt>
#include <string>
#include <locale>
#include <codecvt>
// TODO: rewrite U16toU8 & U8toU16 to not use codecvt
@@ -25,13 +31,15 @@ std::string U16toU8(char16_t* src);
int U8toU16(std::string src, char16_t* des); // returns number of char16_t that was written at des
uint64_t getTime();
//#define CNPROTO_VERSION_0728
#define CNPROTO_VERSION_0104
#if defined(CNPROTO_VERSION_0104)
#include "structs/0104.hpp"
#elif defined(CNPROTO_VERSION_0728)
#include "structs/0728.hpp"
// The PROTOCOL_VERSION definition is defined by the build system.
#if !defined(PROTOCOL_VERSION)
#include "structs/0104.hpp"
#elif PROTOCOL_VERSION == 728
#include "structs/0728.hpp"
#elif PROTOCOL_VERSION == 104
#include "structs/0104.hpp"
#else
#error Invalid PROTOCOL_VERSION
#endif
#endif

View File

@@ -9,6 +9,9 @@ void ChatManager::init() {
}
void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE))
return; // malformed packet
sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE*)data->buf;
PlayerView plr = PlayerManager::players[sock];

129
src/ItemManager.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include "CNShardServer.hpp"
#include "CNStructs.hpp"
#include "ItemManager.hpp"
#include "PlayerManager.hpp"
#include "Player.hpp"
void ItemManager::init() {
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_GIVE_ITEM, itemGMGiveHandler);
}
void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_ITEM_MOVE))
return; // ignore the malformed packet
sP_CL2FE_REQ_ITEM_MOVE* itemmove = (sP_CL2FE_REQ_ITEM_MOVE*)data->buf;
sP_FE2CL_PC_ITEM_MOVE_SUCC* resp = (sP_FE2CL_PC_ITEM_MOVE_SUCC*)xmalloc(sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC));
PlayerView& plr = PlayerManager::players[sock];
sItemBase fromItem;
sItemBase toItem;
// eFrom 0 means from equip
if (itemmove->eFrom == 0) {
// unequiping an item
fromItem = plr.plr.Equip[itemmove->iFromSlotNum];
} else {
fromItem = plr.plr.Inven[itemmove->iFromSlotNum];
}
// eTo 0 means to equip
if (itemmove->eTo == 0) {
// equiping an item
toItem = plr.plr.Equip[itemmove->iToSlotNum];
plr.plr.Equip[itemmove->iToSlotNum] = fromItem;
} else {
toItem = plr.plr.Inven[itemmove->iToSlotNum];
plr.plr.Inven[itemmove->iToSlotNum] = fromItem;
}
if (itemmove->eFrom == 0) {
plr.plr.Equip[itemmove->iFromSlotNum] = toItem;
} else {
plr.plr.Inven[itemmove->iFromSlotNum] = toItem;
}
if (itemmove->eFrom == 0 || itemmove->eTo == 0) {
for (CNSocket* otherSock : plr.viewable) {
sP_FE2CL_PC_EQUIP_CHANGE* resp2 = (sP_FE2CL_PC_EQUIP_CHANGE*)xmalloc(sizeof(sP_FE2CL_PC_EQUIP_CHANGE));
resp2->iPC_ID = plr.plr.iID;
if (itemmove->eFrom == 0) {
resp2->iEquipSlotNum = itemmove->iFromSlotNum;
resp2->EquipSlotItem = toItem;
} else {
resp2->iEquipSlotNum = itemmove->iToSlotNum;
resp2->EquipSlotItem = fromItem;
}
otherSock->sendPacket(new CNPacketData((void*)resp2, P_FE2CL_PC_EQUIP_CHANGE, sizeof(sP_FE2CL_PC_EQUIP_CHANGE), otherSock->getFEKey()));
}
}
resp->eTo = itemmove->eFrom;
resp->iToSlotNum = itemmove->iFromSlotNum;
resp->ToSlotItem = toItem;
resp->eFrom = itemmove->eTo;
resp->iFromSlotNum = itemmove->iToSlotNum;
resp->FromSlotItem = fromItem;
sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_PC_ITEM_MOVE_SUCC, sizeof(sP_FE2CL_PC_ITEM_MOVE_SUCC), sock->getFEKey()));
}
void ItemManager::itemDeleteHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_ITEM_DELETE))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_ITEM_DELETE* itemdel = (sP_CL2FE_REQ_PC_ITEM_DELETE*)data->buf;
sP_FE2CL_REP_PC_ITEM_DELETE_SUCC* resp = (sP_FE2CL_REP_PC_ITEM_DELETE_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC));
PlayerView& plr = PlayerManager::players[sock];
resp->eIL = itemdel->eIL;
resp->iSlotNum = itemdel->iSlotNum;
// so, im not sure what this eIL thing does since you always delete items in inventory and not equips
plr.plr.Inven[itemdel->iSlotNum].iID = 0;
plr.plr.Inven[itemdel->iSlotNum].iType = 0;
plr.plr.Inven[itemdel->iSlotNum].iOpt = 0;
sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_ITEM_DELETE_SUCC, sizeof(sP_FE2CL_REP_PC_ITEM_DELETE_SUCC), sock->getFEKey()));
}
void ItemManager::itemGMGiveHandler(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_PC_GIVE_ITEM))
return; // ignore the malformed packet
sP_CL2FE_REQ_PC_GIVE_ITEM* itemreq = (sP_CL2FE_REQ_PC_GIVE_ITEM*)data->buf;
PlayerView& plr = PlayerManager::players[sock];
// Commented and disabled for future use
//if (!plr.plr.IsGM) {
// TODO: send fail packet
// return;
//}
if (itemreq->eIL == 2) {
// Quest item, not a real item, handle this later, stubbed for now
// sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_GIVE_ITEM_FAIL, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_FAIL), sock->getFEKey()));
} else if (itemreq->eIL == 1) {
sP_FE2CL_REP_PC_GIVE_ITEM_SUCC* resp = (sP_FE2CL_REP_PC_GIVE_ITEM_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC));
resp->eIL = itemreq->eIL;
resp->iSlotNum = itemreq->iSlotNum;
resp->Item = itemreq->Item;
plr.plr.Inven[itemreq->iSlotNum] = itemreq->Item;
plr.plr.level = 36;
sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_GIVE_ITEM_SUCC, sizeof(sP_FE2CL_REP_PC_GIVE_ITEM_SUCC), sock->getFEKey()));
sP_FE2CL_REP_PC_CHANGE_LEVEL* resp2 = (sP_FE2CL_REP_PC_CHANGE_LEVEL*)xmalloc(sizeof(sP_FE2CL_REP_PC_CHANGE_LEVEL));
resp2->iPC_ID = plr.plr.iID;
resp2->iPC_Level = 36;
sock->sendPacket(new CNPacketData((void*)resp2, P_FE2CL_REP_PC_CHANGE_LEVEL, sizeof(sP_FE2CL_REP_PC_CHANGE_LEVEL), sock->getFEKey()));
}
}

13
src/ItemManager.hpp Normal file
View File

@@ -0,0 +1,13 @@
#ifndef _IM_HPP
#define _IM_HPP
#include "CNShardServer.hpp"
namespace ItemManager {
void init();
void itemMoveHandler(CNSocket* sock, CNPacketData* data);
void itemDeleteHandler(CNSocket* sock, CNPacketData* data);
void itemGMGiveHandler(CNSocket* sock, CNPacketData* data);
}
#endif

26
src/NPC.hpp Normal file
View File

@@ -0,0 +1,26 @@
#ifndef _NPCCLASS_HPP
#define _NPCCLASS_HPP
#include "CNStructs.hpp"
class BaseNPC {
public:
sNPCAppearanceData appearanceData;
BaseNPC() {};
BaseNPC(int x, int y, int z, int type) {
appearanceData.iX = x;
appearanceData.iY = y;
appearanceData.iZ = z;
appearanceData.iNPCType = type;
appearanceData.iHP = 400;
appearanceData.iAngle = 0;
appearanceData.iConditionBitFlag = 0;
appearanceData.iBarkerType = 0;
// hopefully no collisions happen :eyes:
appearanceData.iNPC_ID = (int32_t)rand();
};
};
#endif

View File

@@ -1,5 +1,86 @@
#include "NPCManager.hpp"
#include "settings.hpp"
#include <cmath>
#include <algorithm>
#include <list>
#include <fstream>
#include "contrib/JSON.hpp"
std::map<int32_t, BaseNPC> NPCManager::NPCs;
void NPCManager::init() {
// load NPCs from NPCs.json into our NPC manager
try {
std::ifstream inFile("NPCs.json");
nlohmann::json jsonData;
// read file into jsonData
inFile >> jsonData;
for (auto& npc : jsonData) {
BaseNPC tmp(npc["x"], npc["y"], npc["z"], npc["id"]);
NPCManager::NPCs[tmp.appearanceData.iNPC_ID] = tmp;
}
std::cout << "populated " << NPCs.size() << " NPCs" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[WARN] Malformed NPC.json file! Reason:" << std::endl << err.what() << std::endl;
}
}
#undef CHECKNPC
void NPCManager::updatePlayerNPCS(CNSocket* sock, PlayerView& view) {
std::list<int32_t> yesView;
std::list<int32_t> noView;
for (auto& pair : NPCs) {
int diffX = abs(view.plr.x - pair.second.appearanceData.iX);
int diffY = abs(view.plr.y - pair.second.appearanceData.iY);
if (diffX < settings::VIEWDISTANCE && diffY < settings::VIEWDISTANCE) {
yesView.push_back(pair.first);
} else {
noView.push_back(pair.first);
}
}
std::list<int32_t>::iterator i = view.viewableNPCs.begin();
while (i != view.viewableNPCs.end()) {
int32_t id = *i;
if (std::find(noView.begin(), noView.end(), id) != noView.end()) {
// it shouldn't be visible, send NPC_EXIT
sP_FE2CL_NPC_EXIT* exitData = (sP_FE2CL_NPC_EXIT*)xmalloc(sizeof(sP_FE2CL_NPC_EXIT));
exitData->iNPC_ID = id;
sock->sendPacket(new CNPacketData((void*)exitData, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT), sock->getFEKey()));
// remove from view
view.viewableNPCs.erase(i++);
}
++i;
}
for (int32_t id : yesView) {
if (std::find(view.viewableNPCs.begin(), view.viewableNPCs.end(), id) == view.viewableNPCs.end()) {
// needs to be added to viewableNPCs! send NPC_ENTER
sP_FE2CL_NPC_ENTER* enterData = (sP_FE2CL_NPC_ENTER*)xmalloc(sizeof(sP_FE2CL_NPC_ENTER));
enterData->NPCAppearanceData = NPCs[id].appearanceData;
sock->sendPacket(new CNPacketData((void*)enterData, P_FE2CL_NPC_ENTER, sizeof(sP_FE2CL_NPC_ENTER), sock->getFEKey()));
view.viewableNPCs.push_back(id);
}
}
PlayerManager::players[sock].viewableNPCs = view.viewableNPCs;
}

View File

@@ -2,13 +2,16 @@
#define _NPCMANAGER_HPP
#include "CNProtocol.hpp"
#include "PlayerManager.hpp"
#include "NPC.hpp"
#include <map>
namespace NPCManager {
extern std::map<int32_t, BaseNPC> NPCs;
void init();
void updatePlayerNPCS(CNSocket* sock, PlayerView& plr);
}
#endif

View File

@@ -21,6 +21,8 @@ struct Player {
int x, y, z, angle;
sItemBase Equip[AEQUIP_COUNT];
sItemBase Inven[AINVEN_COUNT];
bool IsGM;
};
#endif

View File

@@ -11,6 +11,7 @@
struct PlayerView {
std::list<CNSocket*> viewable;
std::list<int32_t> viewableNPCs;
Player plr;
int long lastHeartbeat;
};

25447
src/contrib/JSON.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,9 @@
#include "CNShardServer.hpp"
#include "PlayerManager.hpp"
#include "ChatManager.hpp"
#include "ItemManager.hpp"
#include "NanoManager.hpp"
#include "NPCManager.hpp"
#include "settings.hpp"
@@ -26,10 +28,13 @@ int main() {
}
#endif
settings::init();
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
PlayerManager::init();
ChatManager::init();
ItemManager::init();
NanoManager::init();
NPCManager::init();
std::cout << "[INFO] Starting Server Threads..." << std::endl;
CNLoginServer loginServer(settings::LOGINPORT);
@@ -46,4 +51,4 @@ int main() {
WSACleanup();
#endif
return 0;
}
}

View File

@@ -1,6 +1,6 @@
#include <iostream>
#include "settings.hpp"
#include "INIReader.hpp"
#include "contrib/INIReader.hpp"
// defaults :)
int settings::LOGINPORT = 8001;
@@ -14,7 +14,8 @@ int settings::VIEWDISTANCE = 20000;
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::MOTDSTRING = "Welcome to OpenFusion!";
void settings::init() {
@@ -37,6 +38,8 @@ void settings::init() {
SPAWN_X = reader.GetInteger("shard", "spawnx", SPAWN_X);
SPAWN_Y = reader.GetInteger("shard", "spawny", SPAWN_Y);
SPAWN_Z = reader.GetInteger("shard", "spawnz", SPAWN_Z);
MOTDSTRING = reader.Get("shard", "motd", "Welcome to OpenFusion!");
GMPASS = reader.Get("login", "pass", GMPASS);
NPCJSON = reader.Get("shard", "npcdata", NPCJSON);
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
}

View File

@@ -11,6 +11,8 @@ namespace settings {
extern int SPAWN_Y;
extern int SPAWN_Z;
extern std::string MOTDSTRING;
extern std::string NPCJSON;
extern std::string GMPASS;
void init();
}

View File

@@ -1,6 +1,7 @@
/* genstructs.py */
#define AEQUIP_COUNT 9
#define AINVEN_COUNT 50
#pragma pack(push)

View File

@@ -1,6 +1,7 @@
/* genstructs.py */
#define AEQUIP_COUNT 12
#define AINVEN_COUNT 50
#pragma pack(push)