mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-10-02 04:30:06 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,7 +1,11 @@
|
||||
.vscode
|
||||
.vscode/
|
||||
bin/*
|
||||
notes.txt
|
||||
config.ini
|
||||
*.o
|
||||
tags
|
||||
*~
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
build/
|
||||
.vs/
|
48
CMakeLists.txt
Normal file
48
CMakeLists.txt
Normal 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()
|
22
LICENSE.md
22
LICENSE.md
@@ -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.
|
||||
|
15
Makefile
15
Makefile
@@ -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)
|
||||
|
15
README.md
15
README.md
@@ -1,5 +1,8 @@
|
||||

|
||||
|
||||
[](https://ci.appveyor.com/project/Raymonf/openfusion)
|
||||
[](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
79
appveyor.yml
Normal 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
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
129
src/ItemManager.cpp
Normal 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
13
src/ItemManager.hpp
Normal 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
26
src/NPC.hpp
Normal 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
|
@@ -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;
|
||||
}
|
@@ -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
|
@@ -21,6 +21,8 @@ struct Player {
|
||||
|
||||
int x, y, z, angle;
|
||||
sItemBase Equip[AEQUIP_COUNT];
|
||||
sItemBase Inven[AINVEN_COUNT];
|
||||
bool IsGM;
|
||||
};
|
||||
|
||||
#endif
|
@@ -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
25447
src/contrib/JSON.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* genstructs.py */
|
||||
|
||||
#define AEQUIP_COUNT 9
|
||||
#define AINVEN_COUNT 50
|
||||
|
||||
#pragma pack(push)
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* genstructs.py */
|
||||
|
||||
#define AEQUIP_COUNT 12
|
||||
#define AINVEN_COUNT 50
|
||||
|
||||
#pragma pack(push)
|
||||
|
||||
|
Reference in New Issue
Block a user