mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-10-20 12:21:04 +00:00
Compare commits
9 Commits
patchmap
...
9cdec59810
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9cdec59810 | ||
c636c538eb | |||
d3bef95a7f | |||
![]() |
650f947451 | ||
![]() |
f4b36b8f73 | ||
![]() |
b12aecad63 | ||
![]() |
5bf0c8f3ea | ||
![]() |
2ddc956c9b | ||
![]() |
4f0ae027a5 |
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
version.h
|
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug (Linux)",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/fusion",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Debug (Windows)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/Debug/winfusion.exe",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Release (Windows)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/bin/Release/winfusion.exe",
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
@@ -44,7 +44,7 @@ add_executable(openfusion ${SOURCES})
|
||||
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
|
||||
|
||||
# find sqlite3 and use it
|
||||
find_package(sqlite3 REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
target_include_directories(openfusion PRIVATE ${SQLite3_INCLUDE_DIRS})
|
||||
target_link_libraries(openfusion PRIVATE ${SQLite3_LIBRARIES})
|
||||
|
||||
@@ -56,5 +56,5 @@ set_property(TARGET openfusion PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_S
|
||||
# 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)
|
||||
target_link_libraries(openfusion PRIVATE pthread)
|
||||
endif()
|
||||
|
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM debian:latest
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN apt-get -y update && apt-get install -y \
|
||||
git \
|
||||
clang \
|
||||
make \
|
||||
libsqlite3-dev
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN make -j8
|
||||
|
||||
# tabledata should be copied from the host;
|
||||
# clone it there before building the container
|
||||
#RUN git submodule update --init --recursive
|
||||
|
||||
CMD ["./bin/fusion"]
|
||||
|
||||
LABEL Name=openfusion Version=0.0.1
|
12
config.ini
12
config.ini
@@ -1,8 +1,3 @@
|
||||
# name of the client build the server is targetting.
|
||||
# used for determining which patches to apply.
|
||||
# default is beta-20111013 for Academy, beta-20100104 otherwise.
|
||||
#buildname=beta-20100104
|
||||
|
||||
# verbosity level
|
||||
# 0 = mostly silence
|
||||
# 1 = debug prints and unknown packets
|
||||
@@ -51,6 +46,11 @@ motd=Welcome to OpenFusion!
|
||||
# location of the patch folder
|
||||
#patchdir=tdata/patch/
|
||||
|
||||
# Space-separated list of patch folders in patchdir to load from.
|
||||
# If you uncomment this, note that Academy builds *must* contain 1013,
|
||||
# and pre-Academy builds must *not* contain it.
|
||||
#enabledpatches=1013
|
||||
|
||||
# xdt json filename
|
||||
#xdtdata=xdt.json
|
||||
# NPC json filename
|
||||
@@ -61,8 +61,6 @@ motd=Welcome to OpenFusion!
|
||||
#pathdata=paths.json
|
||||
# drop json filename
|
||||
#dropdata=drops.json
|
||||
# patchmap json filename
|
||||
#patchmapdata=patchmap.json
|
||||
# gruntwork output filename (this is what you submit)
|
||||
#gruntwork=gruntwork.json
|
||||
# location of the database
|
||||
|
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
openfusion:
|
||||
image: openfusion
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile
|
||||
ports:
|
||||
- "23000:23000"
|
||||
- "23001:23001"
|
||||
- "8003:8003"
|
@@ -70,30 +70,36 @@ static void setValuePlayer(CNSocket* sock, CNPacketData* data) {
|
||||
// Handle serverside value-changes
|
||||
switch (setData->iSetValueType) {
|
||||
case 1:
|
||||
plr->HP = setData->iSetValue;
|
||||
response.iSetValue = plr->HP = setData->iSetValue;
|
||||
break;
|
||||
case 2:
|
||||
plr->batteryW = setData->iSetValue;
|
||||
|
||||
// caps
|
||||
if (plr->batteryW > 9999)
|
||||
plr->batteryW = 9999;
|
||||
|
||||
response.iSetValue = plr->batteryW;
|
||||
break;
|
||||
case 3:
|
||||
plr->batteryN = setData->iSetValue;
|
||||
|
||||
// caps
|
||||
if (plr->batteryN > 9999)
|
||||
plr->batteryN = 9999;
|
||||
|
||||
response.iSetValue = plr->batteryN;
|
||||
break;
|
||||
case 4:
|
||||
Missions::updateFusionMatter(sock, setData->iSetValue - plr->fusionmatter);
|
||||
response.iSetValue = plr->fusionmatter;
|
||||
break;
|
||||
case 5:
|
||||
plr->money = setData->iSetValue;
|
||||
response.iSetValue = plr->money = setData->iSetValue;
|
||||
break;
|
||||
}
|
||||
|
||||
response.iPC_ID = setData->iPC_ID;
|
||||
response.iSetValue = setData->iSetValue;
|
||||
response.iSetValueType = setData->iSetValueType;
|
||||
|
||||
sock->sendPacket(response, P_FE2CL_GM_REP_PC_SET_VALUE);
|
||||
|
@@ -358,23 +358,19 @@ static void npcRotateCommand(std::string full, std::vector<std::string>& args, C
|
||||
int angle = (plr->angle + 180) % 360;
|
||||
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, npc->z, npc->instanceID, angle);
|
||||
|
||||
// if it's a gruntwork NPC, rotate in-place
|
||||
if (TableData::RunningMobs.find(npc->appearanceData.iNPC_ID) != TableData::RunningMobs.end()) {
|
||||
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, npc->x, npc->y, npc->z, npc->instanceID, angle);
|
||||
bool isGruntworkNpc = true;
|
||||
|
||||
Chat::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) + " for gruntwork NPC "
|
||||
+ std::to_string(npc->appearanceData.iNPC_ID));
|
||||
} else {
|
||||
// add a rotation entry to the gruntwork file, unless it's already a gruntwork NPC
|
||||
if (TableData::RunningMobs.find(npc->appearanceData.iNPC_ID) == TableData::RunningMobs.end()) {
|
||||
TableData::RunningNPCRotations[npc->appearanceData.iNPC_ID] = angle;
|
||||
|
||||
Chat::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) + " for NPC "
|
||||
+ std::to_string(npc->appearanceData.iNPC_ID));
|
||||
isGruntworkNpc = false;
|
||||
}
|
||||
|
||||
// update rotation clientside
|
||||
INITSTRUCT(sP_FE2CL_NPC_ENTER, pkt);
|
||||
pkt.NPCAppearanceData = npc->appearanceData;
|
||||
sock->sendPacket(pkt, P_FE2CL_NPC_ENTER);
|
||||
Chat::sendServerMessage(sock, "[NPCR] Successfully set angle to " + std::to_string(angle) +
|
||||
" for " + (isGruntworkNpc ? "gruntwork " : "") + "NPC " + std::to_string(npc->appearanceData.iNPC_ID));
|
||||
|
||||
// update rotation clientside by refreshing the player's chunks (same as the /refresh command)
|
||||
PlayerManager::updatePlayerPositionForWarp(sock, plr->x, plr->y, plr->z, plr->instanceID);
|
||||
}
|
||||
|
||||
static void refreshCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
|
||||
|
@@ -64,7 +64,7 @@ static void racingCancel(CNSocket* sock, CNPacketData* data) {
|
||||
INITSTRUCT(sP_FE2CL_REP_EP_RACE_CANCEL_SUCC, resp);
|
||||
sock->sendPacket(resp, P_FE2CL_REP_EP_RACE_CANCEL_SUCC);
|
||||
|
||||
/*
|
||||
/*
|
||||
* This request packet is used for both cancelling the race via the
|
||||
* NPC at the start, *and* failing the race by running out of time.
|
||||
* If the latter is to happen, the client disables movement until it
|
||||
@@ -97,31 +97,47 @@ static void racingEnd(CNSocket* sock, CNPacketData* data) {
|
||||
if (EPData.find(mapNum) == EPData.end() || EPData[mapNum].EPID == 0)
|
||||
return; // IZ not found
|
||||
|
||||
uint64_t now = getTime() / 1000;
|
||||
EPInfo& epInfo = EPData[mapNum];
|
||||
EPRace& epRace = EPRaces[sock];
|
||||
// if there are no divide-by-zero dangers, and at least one factor has been specified
|
||||
// we switch over to OG scoring
|
||||
bool useOGScoring = (epInfo.maxPods > 0) && (epInfo.maxTime > 0) && (
|
||||
(epInfo.scaleFactor > 0.0) || (epInfo.podFactor > 0.0) || (epInfo.timeFactor > 0.0));
|
||||
|
||||
int timeDiff = now - EPRaces[sock].startTime;
|
||||
int score = 500 * EPRaces[sock].collectedRings.size() - 10 * timeDiff;
|
||||
if (score < 0) score = 0; // lol
|
||||
int fm = score * plr->level * (1.0f / 36) * 0.3f;
|
||||
uint64_t now = getTime() / 1000;
|
||||
int timeDiff = now - epRace.startTime;
|
||||
int podsCollected = epRace.collectedRings.size();
|
||||
int score = 0, fm = 0;
|
||||
|
||||
if (useOGScoring) {
|
||||
score = std::min(epInfo.maxScore, (int)std::exp(
|
||||
(epInfo.podFactor * podsCollected) / epInfo.maxPods
|
||||
- (epInfo.timeFactor * timeDiff) / epInfo.maxTime
|
||||
+ epInfo.scaleFactor));
|
||||
fm = (1.0 + std::exp(epInfo.scaleFactor - 1.0) * epInfo.podFactor * podsCollected) / epInfo.maxPods;
|
||||
} else {
|
||||
score = std::max(0, 500 * podsCollected - 10 * timeDiff);
|
||||
fm = score * plr->level * (1.0f / 36) * 0.3f;
|
||||
}
|
||||
|
||||
// we submit the ranking first...
|
||||
Database::RaceRanking postRanking = {};
|
||||
postRanking.EPID = EPData[mapNum].EPID;
|
||||
postRanking.EPID = epInfo.EPID;
|
||||
postRanking.PlayerID = plr->iID;
|
||||
postRanking.RingCount = EPRaces[sock].collectedRings.size();
|
||||
postRanking.RingCount = podsCollected;
|
||||
postRanking.Score = score;
|
||||
postRanking.Time = timeDiff;
|
||||
postRanking.Timestamp = getTimestamp();
|
||||
Database::postRaceRanking(postRanking);
|
||||
|
||||
// ...then we get the top ranking, which may or may not be what we just submitted
|
||||
Database::RaceRanking topRankingPlayer = Database::getTopRaceRanking(EPData[mapNum].EPID, plr->iID);
|
||||
Database::RaceRanking topRankingPlayer = Database::getTopRaceRanking(epInfo.EPID, plr->iID);
|
||||
|
||||
INITSTRUCT(sP_FE2CL_REP_EP_RACE_END_SUCC, resp);
|
||||
|
||||
// get rank scores and rewards
|
||||
std::vector<int>* rankScores = &EPRewards[EPData[mapNum].EPID].first;
|
||||
std::vector<int>* rankRewards = &EPRewards[EPData[mapNum].EPID].second;
|
||||
std::vector<int>* rankScores = &EPRewards[epInfo.EPID].first;
|
||||
std::vector<int>* rankRewards = &EPRewards[epInfo.EPID].second;
|
||||
|
||||
// top ranking
|
||||
int topRank = 0;
|
||||
|
@@ -5,7 +5,11 @@
|
||||
#include "servers/CNShardServer.hpp"
|
||||
|
||||
struct EPInfo {
|
||||
int zoneX, zoneY, EPID, maxScore, maxTime;
|
||||
// available through XDT (maxScore may be updated by drops)
|
||||
int zoneX, zoneY, EPID, maxScore;
|
||||
// (maybe) available through drops
|
||||
int maxTime = 0, maxPods = 0;
|
||||
double scaleFactor = 0.0, podFactor = 0.0, timeFactor = 0.0;
|
||||
};
|
||||
|
||||
struct EPRace {
|
||||
|
@@ -363,7 +363,7 @@ static void loadPaths(json& pathData, int32_t* nextId) {
|
||||
Transport::NPCPaths.push_back(pathTemplate);
|
||||
}
|
||||
std::cout << "[INFO] Loaded " << Transport::NPCPaths.size() << " NPC paths" << std::endl;
|
||||
|
||||
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
std::cerr << "[FATAL] Malformed paths.json file! Reason:" << err.what() << std::endl;
|
||||
@@ -572,8 +572,35 @@ static void loadDrops(json& dropData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EPInfo& epInfo = Racing::EPData[EPMap];
|
||||
|
||||
// time limit isn't stored in the XDT, so we include it in the reward table instead
|
||||
Racing::EPData[EPMap].maxTime = race["TimeLimit"];
|
||||
epInfo.maxTime = (int)race["TimeLimit"];
|
||||
|
||||
// update max score (if present)
|
||||
if (race.find("ScoreCap") != race.end()) {
|
||||
epInfo.maxScore = (int)race["ScoreCap"];
|
||||
}
|
||||
|
||||
// update max pods (if present)
|
||||
if (race.find("TotalPods") != race.end()) {
|
||||
epInfo.maxPods = (int)race["TotalPods"];
|
||||
}
|
||||
|
||||
// update scale factor (if present)
|
||||
if (race.find("ScaleFactor") != race.end()) {
|
||||
epInfo.scaleFactor = (double)race["ScaleFactor"];
|
||||
}
|
||||
|
||||
// update pod factor (if present)
|
||||
if (race.find("PodFactor") != race.end()) {
|
||||
epInfo.podFactor = (double)race["PodFactor"];
|
||||
}
|
||||
|
||||
// update time factor (if present)
|
||||
if (race.find("TimeFactor") != race.end()) {
|
||||
epInfo.timeFactor = (double)race["TimeFactor"];
|
||||
}
|
||||
|
||||
// score cutoffs
|
||||
std::vector<int> rankScores;
|
||||
@@ -674,7 +701,7 @@ static void loadEggs(json& eggData, int32_t* nextId) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Load gruntwork output, if it exists
|
||||
*/
|
||||
static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
||||
@@ -1086,33 +1113,6 @@ static void patchJSON(json* base, json* patch) {
|
||||
|
||||
void TableData::init() {
|
||||
int32_t nextId = INT32_MAX; // next dynamic ID to hand out
|
||||
json patchmap;
|
||||
|
||||
// load patch map
|
||||
{
|
||||
std::fstream fstream;
|
||||
fstream.open(settings::TDATADIR + "/" + settings::PATCHMAPJSON);
|
||||
|
||||
if (fstream.fail()) {
|
||||
std::cerr << "[FATAL] Critical tdata file missing: " << settings::PATCHMAPJSON << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fstream.peek() == std::ifstream::traits_type::eof()) {
|
||||
std::cerr << "[FATAL] Critical tdata file is empty: " << settings::PATCHMAPJSON << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fstream >> patchmap;
|
||||
fstream.close();
|
||||
}
|
||||
|
||||
// ensure that there is a patch list for the current build
|
||||
if (patchmap["patchmap"].find(settings::BUILDNAME) == patchmap["patchmap"].end()) {
|
||||
std::cerr << "[FATAL] Build name " << settings::BUILDNAME << " not found in " <<
|
||||
settings::PATCHMAPJSON << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// base JSON tables
|
||||
json xdt, paths, drops, eggs, npcs, mobs, gruntwork;
|
||||
@@ -1161,13 +1161,15 @@ void TableData::init() {
|
||||
fstream >> *table.first;
|
||||
}
|
||||
|
||||
// patching: load each patch directory specified in patchmap.json
|
||||
// patching: load each patch directory specified in the config file
|
||||
|
||||
// split config field into individual patch entries
|
||||
std::stringstream ss(settings::ENABLEDPATCHES);
|
||||
std::istream_iterator<std::string> begin(ss);
|
||||
std::istream_iterator<std::string> end;
|
||||
|
||||
// fetch list of patches that need to be applied for the current build
|
||||
json patch;
|
||||
json patchlist = patchmap["patchmap"][settings::BUILDNAME];
|
||||
|
||||
for (auto it = patchlist.begin(); it != patchlist.end(); it++) {
|
||||
for (auto it = begin; it != end; it++) {
|
||||
// this is the theoretical path of a corresponding patch for this file
|
||||
std::string patchModuleName = *it;
|
||||
std::string patchFile = settings::PATCHDIR + patchModuleName + "/" + table.second;
|
||||
@@ -1374,7 +1376,7 @@ void TableData::flush() {
|
||||
targetIDs.push_back(tID);
|
||||
for (int32_t tType : path.targetTypes)
|
||||
targetTypes.push_back(tType);
|
||||
|
||||
|
||||
pathObj["iBaseSpeed"] = path.speed;
|
||||
pathObj["iTaskID"] = path.escortTaskID;
|
||||
pathObj["bRelative"] = path.isRelative;
|
||||
|
@@ -47,13 +47,12 @@ std::string settings::GRUNTWORKJSON = "gruntwork.json";
|
||||
std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
|
||||
std::string settings::DROPSJSON = "drops.json";
|
||||
std::string settings::PATHJSON = "paths.json";
|
||||
std::string settings::PATCHMAPJSON = "patchmap.json";
|
||||
#ifdef ACADEMY
|
||||
std::string settings::XDTJSON = "xdt1013.json";
|
||||
std::string settings::BUILDNAME = "beta-20111013";
|
||||
std::string settings::ENABLEDPATCHES = "1013";
|
||||
#else
|
||||
std::string settings::XDTJSON = "xdt.json";
|
||||
std::string settings::BUILDNAME = "beta-20100104";
|
||||
std::string settings::ENABLEDPATCHES = "";
|
||||
#endif // ACADEMY
|
||||
|
||||
int settings::ACCLEVEL = 1;
|
||||
@@ -79,7 +78,6 @@ void settings::init() {
|
||||
return;
|
||||
}
|
||||
|
||||
BUILDNAME = reader.Get("", "buildname", BUILDNAME);
|
||||
VERBOSITY = reader.GetInteger("", "verbosity", VERBOSITY);
|
||||
SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX);
|
||||
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
|
||||
@@ -107,7 +105,7 @@ void settings::init() {
|
||||
DBPATH = reader.Get("shard", "dbpath", DBPATH);
|
||||
TDATADIR = reader.Get("shard", "tdatadir", TDATADIR);
|
||||
PATCHDIR = reader.Get("shard", "patchdir", PATCHDIR);
|
||||
PATCHMAPJSON = reader.Get("shard", "patchmapdata", PATCHMAPJSON);
|
||||
ENABLEDPATCHES = reader.Get("shard", "enabledpatches", ENABLEDPATCHES);
|
||||
ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL);
|
||||
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
|
||||
DISABLEFIRSTUSEFLAG = reader.GetBoolean("shard", "disablefirstuseflag", DISABLEFIRSTUSEFLAG);
|
||||
|
@@ -29,8 +29,7 @@ namespace settings {
|
||||
extern std::string GRUNTWORKJSON;
|
||||
extern std::string DBPATH;
|
||||
extern std::string PATCHDIR;
|
||||
extern std::string PATCHMAPJSON;
|
||||
extern std::string BUILDNAME;
|
||||
extern std::string ENABLEDPATCHES;
|
||||
extern std::string TDATADIR;
|
||||
extern int EVENTMODE;
|
||||
extern bool MONITORENABLED;
|
||||
|
Reference in New Issue
Block a user