mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-10-27 14:50:05 +00:00
Compare commits
28 Commits
7471bcbf38
...
win32-x86
| Author | SHA1 | Date | |
|---|---|---|---|
| 630e7a69b1 | |||
| eb72dc5f2e | |||
| a7993bfc10 | |||
| a716883971 | |||
| bd0cc3c212 | |||
| c636c538eb | |||
| d3bef95a7f | |||
|
|
650f947451 | ||
|
|
b12aecad63 | ||
|
|
5bf0c8f3ea | ||
|
|
2ddc956c9b | ||
|
|
4f0ae027a5 | ||
| 23ab908366 | |||
| be6a4c0a5d | |||
| 8eb1af20c8 | |||
| e73daa0865 | |||
| 743a39c125 | |||
| a9af8713bc | |||
| 4825267537 | |||
| a92cfaff25 | |||
| abcfa3445b | |||
| 2bf14200f7 | |||
| 876a9c82cd | |||
| fb5b0eeeb9 | |||
| 7aabc507e7 | |||
| 2914b95cff | |||
| dbd2ec2270 | |||
| 50e00a6772 |
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
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 OpenFusion Contributors
|
||||
Copyright (c) 2020-2023 OpenFusion Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -83,7 +83,7 @@ This just works if you're all under the same LAN, but if you want to play over t
|
||||
|
||||
## Compiling
|
||||
|
||||
OpenFusion has one external dependency: SQLite. You can install it on Windows using `vcpkg`, and on Unix/Linux using your distribution's package manager. For a more indepth guide on how to set up vcpkg, [read this guide on the wiki](https://github.com/OpenFusionProject/OpenFusion/wiki/Installing-SQLite-on-Windows-using-vcpkg).
|
||||
OpenFusion has one external dependency: SQLite. The oldest compatible version is `3.33.0`. You can install it on Windows using `vcpkg`, and on Unix/Linux using your distribution's package manager. For a more indepth guide on how to set up vcpkg, [read this guide on the wiki](https://github.com/OpenFusionProject/OpenFusion/wiki/Installing-SQLite-on-Windows-using-vcpkg).
|
||||
|
||||
You have two choices for compiling OpenFusion: the included Makefile and the included CMakeLists file.
|
||||
|
||||
|
||||
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"
|
||||
@@ -546,7 +546,7 @@ bool doDamageNDebuff(Mob *mob, sSkillResult_Damage_N_Debuff *respdata, int i, in
|
||||
|
||||
respdata[i].bProtected = 0;
|
||||
std::pair<CNSocket*, int32_t> key = std::make_pair(sock, bitFlag);
|
||||
time_t until = getTime() + (time_t)duration * 100;
|
||||
int64_t until = getTime() + (int64_t)duration * 100;
|
||||
Eggs::EggBuffs[key] = until;
|
||||
}
|
||||
respdata[i].iConditionBitFlag = plr->iConditionBitFlag;
|
||||
|
||||
@@ -69,31 +69,41 @@ static void setValuePlayer(CNSocket* sock, CNPacketData* data) {
|
||||
|
||||
// Handle serverside value-changes
|
||||
switch (setData->iSetValueType) {
|
||||
case 1:
|
||||
plr->HP = setData->iSetValue;
|
||||
case CN_GM_SET_VALUE_TYPE__HP:
|
||||
response.iSetValue = plr->HP = setData->iSetValue;
|
||||
break;
|
||||
case 2:
|
||||
case CN_GM_SET_VALUE_TYPE__WEAPON_BATTERY :
|
||||
plr->batteryW = setData->iSetValue;
|
||||
|
||||
// caps
|
||||
if (plr->batteryW > 9999)
|
||||
plr->batteryW = 9999;
|
||||
|
||||
response.iSetValue = plr->batteryW;
|
||||
break;
|
||||
case 3:
|
||||
case CN_GM_SET_VALUE_TYPE__NANO_BATTERY:
|
||||
plr->batteryN = setData->iSetValue;
|
||||
|
||||
// caps
|
||||
if (plr->batteryN > 9999)
|
||||
plr->batteryN = 9999;
|
||||
|
||||
response.iSetValue = plr->batteryN;
|
||||
break;
|
||||
case 4:
|
||||
case CN_GM_SET_VALUE_TYPE__FUSION_MATTER:
|
||||
Missions::updateFusionMatter(sock, setData->iSetValue - plr->fusionmatter);
|
||||
response.iSetValue = plr->fusionmatter;
|
||||
break;
|
||||
case 5:
|
||||
plr->money = setData->iSetValue;
|
||||
case CN_GM_SET_VALUE_TYPE__CANDY:
|
||||
response.iSetValue = plr->money = setData->iSetValue;
|
||||
break;
|
||||
case CN_GM_SET_VALUE_TYPE__SPEED:
|
||||
case CN_GM_SET_VALUE_TYPE__JUMP:
|
||||
response.iSetValue = 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);
|
||||
|
||||
@@ -58,7 +58,7 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
|
||||
|
||||
static bool checkRapidFire(CNSocket *sock, int targetCount) {
|
||||
Player *plr = PlayerManager::getPlayer(sock);
|
||||
time_t currTime = getTime();
|
||||
int64_t currTime = getTime();
|
||||
|
||||
if (currTime - plr->lastShot < plr->fireRate * 80)
|
||||
plr->suspicionRating += plr->fireRate * 100 + plr->lastShot - currTime; // gain suspicion for rapid firing
|
||||
@@ -69,7 +69,7 @@ static bool checkRapidFire(CNSocket *sock, int targetCount) {
|
||||
|
||||
// 3+ targets should never be possible
|
||||
if (targetCount > 3)
|
||||
plr->suspicionRating += 10000;
|
||||
plr->suspicionRating += 10001;
|
||||
|
||||
// kill the socket when the player is too suspicious
|
||||
if (plr->suspicionRating > 10000) {
|
||||
@@ -158,7 +158,7 @@ static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
||||
PlayerManager::sendToViewable(sock, respbuf, P_FE2CL_PC_ATTACK_NPCs);
|
||||
}
|
||||
|
||||
void Combat::npcAttackPc(Mob *mob, time_t currTime) {
|
||||
void Combat::npcAttackPc(Mob *mob, int64_t currTime) {
|
||||
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||
|
||||
INITVARPACKET(respbuf, sP_FE2CL_NPC_ATTACK_PCs, pkt, sAttackResult, atk);
|
||||
@@ -654,7 +654,7 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
||||
}
|
||||
|
||||
// rapid fire anti-cheat
|
||||
time_t currTime = getTime();
|
||||
int64_t currTime = getTime();
|
||||
if (currTime - plr->lastShot < plr->fireRate * 80)
|
||||
plr->suspicionRating += plr->fireRate * 100 + plr->lastShot - currTime; // gain suspicion for rapid firing
|
||||
else if (currTime - plr->lastShot < plr->fireRate * 180 && plr->suspicionRating > 0)
|
||||
@@ -726,8 +726,8 @@ static void projectileHit(CNSocket* sock, CNPacketData* data) {
|
||||
Bullets[plr->iID].erase(resp->iBulletID);
|
||||
}
|
||||
|
||||
static void playerTick(CNServer *serv, time_t currTime) {
|
||||
static time_t lastHealTime = 0;
|
||||
static void playerTick(CNServer *serv, int64_t currTime) {
|
||||
static int64_t lastHealTime = 0;
|
||||
|
||||
for (auto& pair : PlayerManager::players) {
|
||||
CNSocket *sock = pair.first;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Combat {
|
||||
|
||||
void init();
|
||||
|
||||
void npcAttackPc(Mob *mob, time_t currTime);
|
||||
void npcAttackPc(Mob *mob, int64_t currTime);
|
||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
||||
void killMob(CNSocket *sock, Mob *mob);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
10
src/Eggs.cpp
10
src/Eggs.cpp
@@ -11,7 +11,7 @@
|
||||
using namespace Eggs;
|
||||
|
||||
/// sock, CBFlag -> until
|
||||
std::map<std::pair<CNSocket*, int32_t>, time_t> Eggs::EggBuffs;
|
||||
std::map<std::pair<CNSocket*, int32_t>, int64_t> Eggs::EggBuffs;
|
||||
std::unordered_map<int, EggType> Eggs::EggTypes;
|
||||
|
||||
int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
|
||||
@@ -79,15 +79,15 @@ int Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
|
||||
|
||||
// save the buff serverside;
|
||||
// if you get the same buff again, new duration will override the previous one
|
||||
time_t until = getTime() + (time_t)duration * 1000;
|
||||
int64_t until = getTime() + (int64_t)duration * 1000;
|
||||
EggBuffs[key] = until;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eggStep(CNServer* serv, time_t currTime) {
|
||||
static void eggStep(CNServer* serv, int64_t currTime) {
|
||||
// tick buffs
|
||||
time_t timeStamp = currTime;
|
||||
int64_t timeStamp = currTime;
|
||||
auto it = EggBuffs.begin();
|
||||
while (it != EggBuffs.end()) {
|
||||
// check remaining time
|
||||
@@ -254,7 +254,7 @@ static void eggPickup(CNSocket* sock, CNPacketData* data) {
|
||||
else {
|
||||
Chunking::removeEntityFromChunks(Chunking::getViewableChunks(egg->chunkPos), eggRef);
|
||||
egg->dead = true;
|
||||
egg->deadUntil = getTime() + (time_t)type->regen * 1000;
|
||||
egg->deadUntil = getTime() + (int64_t)type->regen * 1000;
|
||||
egg->appearanceData.iHP = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ struct EggType {
|
||||
};
|
||||
|
||||
namespace Eggs {
|
||||
extern std::map<std::pair<CNSocket*, int32_t>, time_t> EggBuffs;
|
||||
extern std::map<std::pair<CNSocket*, int32_t>, int64_t> EggBuffs;
|
||||
extern std::unordered_map<int, EggType> EggTypes;
|
||||
|
||||
void init();
|
||||
|
||||
@@ -252,11 +252,26 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
|
||||
if (attachment.ItemInven.iID == 0)
|
||||
continue;
|
||||
|
||||
sItemBase* item = &pkt->aItem[i].ItemInven;
|
||||
sItemBase* real = &plr->Inven[attachment.iSlotNum];
|
||||
|
||||
resp.aItem[i] = attachment;
|
||||
attachments.push_back(attachment.ItemInven);
|
||||
attSlots.push_back(attachment.iSlotNum);
|
||||
// delete item
|
||||
plr->Inven[attachment.iSlotNum] = { 0, 0, 0, 0 };
|
||||
if (real->iOpt <= item->iOpt) // delete item (if they attached the whole stack)
|
||||
*real = { 0, 0, 0, 0 };
|
||||
else // otherwise, decrement the item
|
||||
real->iOpt -= item->iOpt;
|
||||
|
||||
// HACK: update the slot
|
||||
INITSTRUCT(sP_FE2CL_PC_ITEM_MOVE_SUCC, itemResp);
|
||||
itemResp.iFromSlotNum = attachment.iSlotNum;
|
||||
itemResp.iToSlotNum = attachment.iSlotNum;
|
||||
itemResp.FromSlotItem = *real;
|
||||
itemResp.ToSlotItem = *real;
|
||||
itemResp.eFrom = (int32_t)Items::SlotType::INVENTORY;
|
||||
itemResp.eTo = (int32_t)Items::SlotType::INVENTORY;
|
||||
sock->sendPacket(itemResp, P_FE2CL_PC_ITEM_MOVE_SUCC);
|
||||
}
|
||||
|
||||
int cost = pkt->iCash + 50 + 20 * attachments.size(); // attached taros + postage
|
||||
|
||||
@@ -103,14 +103,14 @@ struct CombatNPC : public BaseNPC {
|
||||
int level = 0;
|
||||
int speed = 300;
|
||||
|
||||
void (*_stepAI)(CombatNPC*, time_t) = nullptr;
|
||||
void (*_stepAI)(CombatNPC*, int64_t) = nullptr;
|
||||
|
||||
// XXX
|
||||
CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP) :
|
||||
BaseNPC(x, y, z, angle, iID, t, id),
|
||||
maxHealth(maxHP) {}
|
||||
|
||||
virtual void stepAI(time_t currTime) {
|
||||
virtual void stepAI(int64_t currTime) {
|
||||
if (_stepAI != nullptr)
|
||||
_stepAI(this, currTime);
|
||||
}
|
||||
@@ -124,7 +124,7 @@ struct CombatNPC : public BaseNPC {
|
||||
struct Egg : public BaseNPC {
|
||||
bool summoned = false;
|
||||
bool dead = false;
|
||||
time_t deadUntil;
|
||||
int64_t deadUntil;
|
||||
|
||||
Egg(int x, int y, int z, uint64_t iID, int t, int32_t id, bool summon)
|
||||
: BaseNPC(x, y, z, 0, iID, t, id) {
|
||||
|
||||
@@ -501,7 +501,7 @@ static void itemUseHandler(CNSocket* sock, CNPacketData* data) {
|
||||
player->Inven[resp->iSlotNum] = resp->RemainItem;
|
||||
|
||||
std::pair<CNSocket*, int32_t> key = std::make_pair(sock, value1);
|
||||
time_t until = getTime() + (time_t)Nanos::SkillTable[144].durationTime[0] * 100;
|
||||
int64_t until = getTime() + (int64_t)Nanos::SkillTable[144].durationTime[0] * 100;
|
||||
Eggs::EggBuffs[key] = until;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using namespace MobAI;
|
||||
|
||||
bool MobAI::simulateMobs = settings::SIMULATEMOBS;
|
||||
|
||||
static void roamingStep(Mob *mob, time_t currTime);
|
||||
static void roamingStep(Mob *mob, int64_t currTime);
|
||||
|
||||
/*
|
||||
* Dynamic lerp; distinct from Transport::lerp(). This one doesn't care about height and
|
||||
@@ -119,7 +119,7 @@ void MobAI::groupRetreat(Mob *mob) {
|
||||
* Even if they're in range, we can't assume they're all in the same one chunk
|
||||
* as the mob, since it might be near a chunk boundary.
|
||||
*/
|
||||
bool MobAI::aggroCheck(Mob *mob, time_t currTime) {
|
||||
bool MobAI::aggroCheck(Mob *mob, int64_t currTime) {
|
||||
CNSocket *closest = nullptr;
|
||||
int closestDistance = INT_MAX;
|
||||
|
||||
@@ -281,7 +281,7 @@ static void dealCorruption(Mob *mob, std::vector<int> targetData, int skillID, i
|
||||
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_NPC_SKILL_CORRUPTION_HIT, resplen);
|
||||
}
|
||||
|
||||
static void useAbilities(Mob *mob, time_t currTime) {
|
||||
static void useAbilities(Mob *mob, int64_t currTime) {
|
||||
/*
|
||||
* targetData approach
|
||||
* first integer is the count
|
||||
@@ -441,7 +441,7 @@ static void drainMobHP(Mob *mob, int amount) {
|
||||
Combat::killMob(mob->target, mob);
|
||||
}
|
||||
|
||||
static void deadStep(Mob *mob, time_t currTime) {
|
||||
static void deadStep(Mob *mob, int64_t currTime) {
|
||||
// despawn the mob after a short delay
|
||||
if (mob->killedTime != 0 && !mob->despawned && currTime - mob->killedTime > 2000) {
|
||||
mob->despawned = true;
|
||||
@@ -500,7 +500,7 @@ static void deadStep(Mob *mob, time_t currTime) {
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW));
|
||||
}
|
||||
|
||||
static void combatStep(Mob *mob, time_t currTime) {
|
||||
static void combatStep(Mob *mob, int64_t currTime) {
|
||||
assert(mob->target != nullptr);
|
||||
|
||||
// lose aggro if the player lost connection
|
||||
@@ -542,7 +542,7 @@ static void combatStep(Mob *mob, time_t currTime) {
|
||||
return;
|
||||
|
||||
// unbuffing
|
||||
std::unordered_map<int32_t, time_t>::iterator it = mob->unbuffTimes.begin();
|
||||
std::unordered_map<int32_t, int64_t>::iterator it = mob->unbuffTimes.begin();
|
||||
while (it != mob->unbuffTimes.end()) {
|
||||
|
||||
if (currTime >= it->second) {
|
||||
@@ -640,7 +640,7 @@ static void combatStep(Mob *mob, time_t currTime) {
|
||||
}
|
||||
}
|
||||
|
||||
void MobAI::incNextMovement(Mob *mob, time_t currTime) {
|
||||
void MobAI::incNextMovement(Mob *mob, int64_t currTime) {
|
||||
if (currTime == 0)
|
||||
currTime = getTime();
|
||||
|
||||
@@ -648,7 +648,7 @@ void MobAI::incNextMovement(Mob *mob, time_t currTime) {
|
||||
mob->nextMovement = currTime + delay/2 + Rand::rand(delay/2);
|
||||
}
|
||||
|
||||
static void roamingStep(Mob *mob, time_t currTime) {
|
||||
static void roamingStep(Mob *mob, int64_t currTime) {
|
||||
/*
|
||||
* We reuse nextAttack to avoid scanning for players all the time, but to still
|
||||
* do so more often than if we waited for nextMovement (which is way too slow).
|
||||
@@ -736,7 +736,7 @@ static void roamingStep(Mob *mob, time_t currTime) {
|
||||
}
|
||||
}
|
||||
|
||||
static void retreatStep(Mob *mob, time_t currTime) {
|
||||
static void retreatStep(Mob *mob, int64_t currTime) {
|
||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||
return;
|
||||
|
||||
@@ -781,7 +781,7 @@ static void retreatStep(Mob *mob, time_t currTime) {
|
||||
}
|
||||
}
|
||||
|
||||
void MobAI::step(CombatNPC *npc, time_t currTime) {
|
||||
void MobAI::step(CombatNPC *npc, int64_t currTime) {
|
||||
assert(npc->type == EntityType::MOB);
|
||||
auto mob = (Mob*)npc;
|
||||
|
||||
|
||||
@@ -13,32 +13,32 @@ enum class MobState {
|
||||
|
||||
namespace MobAI {
|
||||
// needs to be declared before Mob's constructor
|
||||
void step(CombatNPC*, time_t);
|
||||
void step(CombatNPC*, int64_t);
|
||||
};
|
||||
|
||||
struct Mob : public CombatNPC {
|
||||
// general
|
||||
MobState state = MobState::INACTIVE;
|
||||
|
||||
std::unordered_map<int32_t,time_t> unbuffTimes = {};
|
||||
std::unordered_map<int32_t,int64_t> unbuffTimes = {};
|
||||
|
||||
// dead
|
||||
time_t killedTime = 0;
|
||||
time_t regenTime = 0;
|
||||
int64_t killedTime = 0;
|
||||
int64_t regenTime = 0;
|
||||
bool summoned = false;
|
||||
bool despawned = false; // for the sake of death animations
|
||||
|
||||
// roaming
|
||||
int idleRange = 0;
|
||||
const int sightRange = 0;
|
||||
time_t nextMovement = 0;
|
||||
int64_t nextMovement = 0;
|
||||
bool staticPath = false;
|
||||
int roamX = 0, roamY = 0, roamZ = 0;
|
||||
|
||||
// combat
|
||||
CNSocket *target = nullptr;
|
||||
time_t nextAttack = 0;
|
||||
time_t lastDrainTime = 0;
|
||||
int64_t nextAttack = 0;
|
||||
int64_t lastDrainTime = 0;
|
||||
int skillStyle = -1; // -1 for nothing, 0-2 for corruption, -2 for eruption
|
||||
int hitX = 0, hitY = 0, hitZ = 0; // for use in ability targeting
|
||||
|
||||
@@ -98,8 +98,8 @@ namespace MobAI {
|
||||
extern bool simulateMobs;
|
||||
|
||||
// TODO: make this internal later
|
||||
void incNextMovement(Mob *mob, time_t currTime=0);
|
||||
bool aggroCheck(Mob *mob, time_t currTime);
|
||||
void incNextMovement(Mob *mob, int64_t currTime=0);
|
||||
bool aggroCheck(Mob *mob, int64_t currTime);
|
||||
void clearDebuff(Mob *mob);
|
||||
void followToCombat(Mob *mob);
|
||||
void groupRetreat(Mob *mob);
|
||||
|
||||
@@ -350,7 +350,7 @@ void NPCManager::queueNPCRemoval(int32_t id) {
|
||||
RemovalQueue.push(id);
|
||||
}
|
||||
|
||||
static void step(CNServer *serv, time_t currTime) {
|
||||
static void step(CNServer *serv, int64_t currTime) {
|
||||
for (auto& pair : NPCs) {
|
||||
if (pair.second->type != EntityType::COMBAT_NPC && pair.second->type != EntityType::MOB)
|
||||
continue;
|
||||
|
||||
@@ -79,10 +79,10 @@ struct Player : public Entity {
|
||||
bool isBuddyBlocked[50] = {};
|
||||
|
||||
uint64_t iFirstUseFlag[2] = {};
|
||||
time_t lastHeartbeat = 0;
|
||||
int64_t lastHeartbeat = 0;
|
||||
|
||||
int suspicionRating = 0;
|
||||
time_t lastShot = 0;
|
||||
int64_t lastShot = 0;
|
||||
std::vector<sItemBase> buyback = {};
|
||||
|
||||
Player() { type = EntityType::PLAYER; }
|
||||
|
||||
@@ -11,7 +11,7 @@ struct EPInfo {
|
||||
struct EPRace {
|
||||
std::set<int> collectedRings;
|
||||
int mode, ticketSlot;
|
||||
time_t startTime;
|
||||
int64_t startTime;
|
||||
};
|
||||
|
||||
namespace Racing {
|
||||
|
||||
@@ -37,6 +37,22 @@ public:
|
||||
const char *what() const throw() { return msg.c_str(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* We must refuse to run if an invalid NPC type is found in the JSONs, especially
|
||||
* the gruntwork file. If we were to just skip loading invalid NPCs, they would get
|
||||
* silently dropped from the gruntwork file, which would be confusing in situations
|
||||
* where a gruntwork file for the wrong game build was accidentally loaded.
|
||||
*/
|
||||
static void ensureValidNPCType(int type, std::string filename) {
|
||||
// last known NPC type
|
||||
int npcLimit = NPCManager::NPCData.back()["m_iNpcNumber"];
|
||||
|
||||
if (type > npcLimit) {
|
||||
std::cout << "[FATAL] " << filename << " contains an invalid NPC type: " << type << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a full and properly-paced path by interpolating between keyframes.
|
||||
*/
|
||||
@@ -662,6 +678,8 @@ static void loadEggs(json& eggData, int32_t* nextId) {
|
||||
* Load gruntwork output, if it exists
|
||||
*/
|
||||
static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
||||
if (gruntwork.is_null())
|
||||
return;
|
||||
|
||||
try {
|
||||
auto paths = gruntwork["paths"];
|
||||
@@ -711,8 +729,8 @@ static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
||||
}
|
||||
|
||||
static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
||||
|
||||
if (gruntwork.is_null()) return;
|
||||
if (gruntwork.is_null())
|
||||
return;
|
||||
|
||||
try {
|
||||
// skyway paths
|
||||
@@ -764,6 +782,8 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
||||
int id = (*nextId)--;
|
||||
uint64_t instanceID = mob.find("iMapNum") == mob.end() ? INSTANCE_OVERWORLD : (int)mob["iMapNum"];
|
||||
|
||||
ensureValidNPCType((int)mob["iNPCType"], settings::GRUNTWORKJSON);
|
||||
|
||||
if (NPCManager::NPCData[(int)mob["iNPCType"]]["m_iTeam"] == 2) {
|
||||
npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"],
|
||||
NPCManager::NPCData[(int)mob["iNPCType"]], id);
|
||||
@@ -783,6 +803,9 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
||||
auto groups = gruntwork["groups"];
|
||||
for (auto _group = groups.begin(); _group != groups.end(); _group++) {
|
||||
auto leader = _group.value();
|
||||
|
||||
ensureValidNPCType((int)leader["iNPCType"], settings::GRUNTWORKJSON);
|
||||
|
||||
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
||||
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
||||
|
||||
@@ -803,6 +826,9 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
||||
int followerCount = 0;
|
||||
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
||||
auto follower = _fol.value();
|
||||
|
||||
ensureValidNPCType((int)follower["iNPCType"], settings::GRUNTWORKJSON);
|
||||
|
||||
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
||||
Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, *nextId);
|
||||
|
||||
@@ -859,10 +885,9 @@ static void loadNPCs(json& npcData) {
|
||||
npcID += NPC_ID_OFFSET;
|
||||
int instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
||||
int type = (int)npc["iNPCType"];
|
||||
if (NPCManager::NPCData[type].is_null()) {
|
||||
std::cout << "[WARN] NPC type " << type << " not found; skipping (json#" << _npc.key() << ")" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
ensureValidNPCType(type, settings::NPCJSON);
|
||||
|
||||
#ifdef ACADEMY
|
||||
// do not spawn NPCs in the future
|
||||
if (npc["iX"] > 512000 && npc["iY"] < 256000)
|
||||
@@ -904,10 +929,9 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
||||
int npcID = std::strtol(_npc.key().c_str(), nullptr, 10); // parse ID string to integer
|
||||
npcID += MOB_ID_OFFSET;
|
||||
int type = (int)npc["iNPCType"];
|
||||
if (NPCManager::NPCData[type].is_null()) {
|
||||
std::cout << "[WARN] NPC type " << type << " not found; skipping (json#" << _npc.key() << ")" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
ensureValidNPCType(type, settings::MOBJSON);
|
||||
|
||||
auto td = NPCManager::NPCData[type];
|
||||
uint64_t instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
||||
|
||||
@@ -935,7 +959,10 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
||||
for (json::iterator _group = groupData.begin(); _group != groupData.end(); _group++) {
|
||||
auto leader = _group.value();
|
||||
int leadID = std::strtol(_group.key().c_str(), nullptr, 10); // parse ID string to integer
|
||||
|
||||
leadID += MOB_GROUP_ID_OFFSET;
|
||||
ensureValidNPCType(leader["iNPCType"], settings::MOBJSON);
|
||||
|
||||
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
||||
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
||||
auto followers = leader["aFollowers"];
|
||||
@@ -965,6 +992,9 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
||||
int followerCount = 0;
|
||||
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
||||
auto follower = _fol.value();
|
||||
|
||||
ensureValidNPCType(follower["iNPCType"], settings::MOBJSON);
|
||||
|
||||
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
||||
Mob* tmpFol = new Mob((int)leader["iX"] + (int)follower["iOffsetX"], (int)leader["iY"] + (int)follower["iOffsetY"], leader["iZ"], leader["iAngle"], instanceID, follower["iNPCType"], tdFol, *nextId);
|
||||
|
||||
@@ -1070,19 +1100,39 @@ void TableData::init() {
|
||||
};
|
||||
|
||||
// load JSON data into tables
|
||||
std::ifstream fstream;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
std::pair<json*, std::string>& table = tables[i];
|
||||
|
||||
// scope for fstream
|
||||
{
|
||||
std::ifstream fstream;
|
||||
fstream.open(settings::TDATADIR + "/" + table.second); // open file
|
||||
if (!fstream.fail()) {
|
||||
fstream >> *table.first; // load file contents into table
|
||||
} else {
|
||||
if (table.first != &gruntwork) { // gruntwork isn't critical
|
||||
|
||||
// did we fail to open the file?
|
||||
if (fstream.fail()) {
|
||||
// gruntwork isn't critical
|
||||
if (table.first == &gruntwork)
|
||||
continue;
|
||||
|
||||
std::cerr << "[FATAL] Critical tdata file missing: " << table.second << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// is the file empty?
|
||||
if (fstream.peek() == std::ifstream::traits_type::eof()) {
|
||||
// tolerate empty gruntwork file
|
||||
if (table.first == &gruntwork) {
|
||||
std::cout << "[WARN] The gruntwork file is empty" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cerr << "[FATAL] Critical tdata file is empty: " << table.second << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// load file contents into table
|
||||
fstream >> *table.first;
|
||||
}
|
||||
fstream.close();
|
||||
|
||||
// patching: load each patch directory specified in the config file
|
||||
|
||||
@@ -1097,11 +1147,11 @@ void TableData::init() {
|
||||
std::string patchModuleName = *it;
|
||||
std::string patchFile = settings::PATCHDIR + patchModuleName + "/" + table.second;
|
||||
try {
|
||||
std::ifstream fstream;
|
||||
fstream.open(patchFile);
|
||||
fstream >> patch; // load into temporary json object
|
||||
std::cout << "[INFO] Patching " << patchFile << std::endl;
|
||||
patchJSON(table.first, &patch); // patch
|
||||
fstream.close();
|
||||
} catch (const std::exception& err) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ static void stepNPCPathing() {
|
||||
}
|
||||
}
|
||||
|
||||
static void tickTransportationSystem(CNServer* serv, time_t currTime) {
|
||||
static void tickTransportationSystem(CNServer* serv, int64_t currTime) {
|
||||
stepNPCPathing();
|
||||
stepSkywaySystem();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ int CNSocketEncryption::xorData(uint8_t* buffer, uint8_t* key, int size) {
|
||||
uint64_t CNSocketEncryption::createNewKey(uint64_t uTime, int32_t iv1, int32_t iv2) {
|
||||
uint64_t num = (uint64_t)(iv1 + 1);
|
||||
uint64_t num2 = (uint64_t)(iv2 + 1);
|
||||
uint64_t dEKey = (uint64_t)(*(uint64_t*)&defaultKey[0]);
|
||||
uint64_t dEKey;
|
||||
memcpy(&dEKey, defaultKey, sizeof(dEKey));
|
||||
return dEKey * (uTime * num * num2);
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ CNPacketData::CNPacketData(void *b, uint32_t t, int l, int trnum, void *trs):
|
||||
// ========================================================[[ CNSocket ]]========================================================
|
||||
|
||||
CNSocket::CNSocket(SOCKET s, struct sockaddr_in &addr, PacketHandler ph): sock(s), sockaddr(addr), pHandler(ph) {
|
||||
EKey = (uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]);
|
||||
memcpy(&EKey, CNSocketEncryption::defaultKey, sizeof(EKey));
|
||||
}
|
||||
|
||||
bool CNSocket::sendData(uint8_t* data, int size) {
|
||||
@@ -109,7 +110,11 @@ bool CNSocket::isAlive() {
|
||||
}
|
||||
|
||||
void CNSocket::kill() {
|
||||
if (!alive)
|
||||
return;
|
||||
|
||||
alive = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
shutdown(sock, SD_BOTH);
|
||||
closesocket(sock);
|
||||
@@ -241,9 +246,10 @@ void CNSocket::step() {
|
||||
if (readSize <= 0) {
|
||||
// we aren't reading a packet yet, try to start looking for one
|
||||
int recved = recv(sock, (buffer_t*)readBuffer, sizeof(int32_t), 0);
|
||||
if (recved == 0) {
|
||||
// the socket was closed normally
|
||||
if (recved >= 0 && recved < sizeof(int32_t)) {
|
||||
// too little data for readSize or the socket was closed normally (when 0 bytes were read)
|
||||
kill();
|
||||
return;
|
||||
} else if (!SOCKETERROR(recved)) {
|
||||
// we got our packet size!!!!
|
||||
readSize = *((int32_t*)readBuffer);
|
||||
@@ -264,11 +270,12 @@ void CNSocket::step() {
|
||||
}
|
||||
|
||||
if (readSize > 0 && readBufferIndex < readSize) {
|
||||
// read until the end of the packet! (or at least try too)
|
||||
// read until the end of the packet (or at least try to)
|
||||
int recved = recv(sock, (buffer_t*)(readBuffer + readBufferIndex), readSize - readBufferIndex, 0);
|
||||
if (recved == 0) {
|
||||
// the socket was closed normally
|
||||
kill();
|
||||
return;
|
||||
} else if (!SOCKETERROR(recved))
|
||||
readBufferIndex += recved;
|
||||
else if (OF_ERRNO != OF_EWOULD) {
|
||||
@@ -411,9 +418,9 @@ void CNServer::addPollFD(SOCKET s) {
|
||||
fds.push_back({s, POLLIN});
|
||||
}
|
||||
|
||||
void CNServer::removePollFD(int i) {
|
||||
void CNServer::removePollFD(int fd) {
|
||||
auto it = fds.begin();
|
||||
while (it != fds.end() && it->fd != fds[i].fd)
|
||||
while (it != fds.end() && it->fd != fd)
|
||||
it++;
|
||||
assert(it != fds.end());
|
||||
|
||||
@@ -458,7 +465,7 @@ void CNServer::start() {
|
||||
if (!setSockNonblocking(sock, newConnectionSocket))
|
||||
continue;
|
||||
|
||||
std::cout << "New connection! " << inet_ntoa(address.sin_addr) << std::endl;
|
||||
std::cout << "New " << serverType << " connection! " << inet_ntoa(address.sin_addr) << std::endl;
|
||||
|
||||
addPollFD(newConnectionSocket);
|
||||
|
||||
@@ -473,10 +480,15 @@ void CNServer::start() {
|
||||
} else {
|
||||
std::lock_guard<std::mutex> lock(activeCrit); // protect operations on connections
|
||||
|
||||
// halt packet handling if server is shutting down
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
// player sockets
|
||||
if (connections.find(fds[i].fd) == connections.end()) {
|
||||
std::cout << "[WARN] Event on non-existant socket?" << std::endl;
|
||||
continue; // just to be safe
|
||||
std::cout << "[FATAL] Event on non-existent socket: " << fds[i].fd << std::endl;
|
||||
assert(0);
|
||||
/* not reached */
|
||||
}
|
||||
|
||||
CNSocket* cSock = connections[fds[i].fd];
|
||||
@@ -485,22 +497,29 @@ void CNServer::start() {
|
||||
if (fds[i].revents & ~POLLIN)
|
||||
cSock->kill();
|
||||
|
||||
if (cSock->isAlive()) {
|
||||
if (cSock->isAlive())
|
||||
cSock->step();
|
||||
} else {
|
||||
killConnection(cSock);
|
||||
connections.erase(fds[i].fd);
|
||||
delete cSock;
|
||||
|
||||
removePollFD(i);
|
||||
|
||||
// a new entry was moved to this position, so we check it again
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onStep();
|
||||
|
||||
// clean up dead connection sockets
|
||||
auto it = connections.begin();
|
||||
while (it != connections.end()) {
|
||||
CNSocket *cSock = it->second;
|
||||
|
||||
if (!cSock->isAlive()) {
|
||||
killConnection(cSock);
|
||||
it = connections.erase(it);
|
||||
|
||||
removePollFD(cSock->sock);
|
||||
|
||||
delete cSock;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,15 +208,15 @@ public:
|
||||
};
|
||||
|
||||
class CNServer;
|
||||
typedef void (*TimerHandler)(CNServer* serv, time_t time);
|
||||
typedef void (*TimerHandler)(CNServer* serv, int64_t time);
|
||||
|
||||
// timer struct
|
||||
struct TimerEvent {
|
||||
TimerHandler handlr;
|
||||
time_t delta; // time to be added to the current time on reset
|
||||
time_t scheduledEvent; // time to call handlr()
|
||||
int64_t delta; // time to be added to the current time on reset
|
||||
int64_t scheduledEvent; // time to call handlr()
|
||||
|
||||
TimerEvent(TimerHandler h, time_t d): handlr(h), delta(d) {
|
||||
TimerEvent(TimerHandler h, int64_t d): handlr(h), delta(d) {
|
||||
scheduledEvent = 0;
|
||||
}
|
||||
};
|
||||
@@ -230,6 +230,7 @@ protected:
|
||||
const size_t STARTFDSCOUNT = 8; // number of initial PollFD slots
|
||||
std::vector<PollFD> fds;
|
||||
|
||||
std::string serverType = "invalid";
|
||||
SOCKET sock;
|
||||
uint16_t port;
|
||||
socklen_t addressSize;
|
||||
|
||||
@@ -24,7 +24,7 @@ LoginMetadata* CNShared::getLoginMetadata(int64_t sk) {
|
||||
return lm;
|
||||
}
|
||||
|
||||
void CNShared::pruneLoginMetadata(CNServer *serv, time_t currTime) {
|
||||
void CNShared::pruneLoginMetadata(CNServer *serv, int64_t currTime) {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
|
||||
auto it = logins.begin();
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
struct LoginMetadata {
|
||||
uint64_t FEKey;
|
||||
int32_t playerId;
|
||||
time_t timestamp;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
namespace CNShared {
|
||||
void storeLoginMetadata(int64_t sk, LoginMetadata *lm);
|
||||
LoginMetadata* getLoginMetadata(int64_t sk);
|
||||
void pruneLoginMetadata(CNServer *serv, time_t currTime);
|
||||
void pruneLoginMetadata(CNServer *serv, int64_t currTime);
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
|
||||
std::string U16toU8(char16_t* src, size_t max);
|
||||
size_t U8toU16(std::string src, char16_t* des, size_t max); // returns number of char16_t that was written at des
|
||||
time_t getTime();
|
||||
time_t getTimestamp();
|
||||
int64_t getTime();
|
||||
int64_t getTimestamp();
|
||||
void terminate(int);
|
||||
|
||||
// The PROTOCOL_VERSION definition is defined by the build system.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Database {
|
||||
int AccountID;
|
||||
std::string Password;
|
||||
int Selected;
|
||||
time_t BannedUntil;
|
||||
int64_t BannedUntil;
|
||||
std::string BanReason;
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace Database {
|
||||
uint64_t Timestamp;
|
||||
};
|
||||
|
||||
void init();
|
||||
void open();
|
||||
void close();
|
||||
|
||||
|
||||
@@ -236,7 +236,20 @@ static int getTableSize(std::string tableName) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Database::init() {
|
||||
std::cout << "[INFO] Built with libsqlite " SQLITE_VERSION << std::endl;
|
||||
|
||||
if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER)
|
||||
std::cout << "[INFO] Using libsqlite " << std::string(sqlite3_libversion()) << std::endl;
|
||||
|
||||
if (sqlite3_libversion_number() < MIN_SUPPORTED_SQLITE_NUMBER) {
|
||||
std::cerr << "[FATAL] Runtime sqlite version too old. Minimum compatible version: " MIN_SUPPORTED_SQLITE << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Database::open() {
|
||||
|
||||
// XXX: move locks here
|
||||
int rc = sqlite3_open(settings::DBPATH.c_str(), &db);
|
||||
if (rc != SQLITE_OK) {
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
#include "db/Database.hpp"
|
||||
#include <sqlite3.h>
|
||||
|
||||
#define MIN_SUPPORTED_SQLITE_NUMBER 3033000
|
||||
#define MIN_SUPPORTED_SQLITE "3.33.0"
|
||||
// we can't use this in #error, since it doesn't expand macros
|
||||
|
||||
// Compile-time libsqlite version check
|
||||
#if SQLITE_VERSION_NUMBER < MIN_SUPPORTED_SQLITE_NUMBER
|
||||
#error libsqlite version too old. Minimum compatible version: 3.33.0
|
||||
#endif
|
||||
|
||||
extern std::mutex dbCrit;
|
||||
extern sqlite3 *db;
|
||||
|
||||
|
||||
26
src/main.cpp
26
src/main.cpp
@@ -64,7 +64,7 @@ void terminate(int arg) {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static BOOL winTerminate(DWORD arg) {
|
||||
static BOOL WINAPI winTerminate(DWORD arg) {
|
||||
terminate(0);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -98,6 +98,9 @@ void initsignals() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl;
|
||||
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
|
||||
@@ -105,15 +108,15 @@ int main() {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
initsignals();
|
||||
settings::init();
|
||||
|
||||
std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl;
|
||||
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
|
||||
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
|
||||
|
||||
Database::init();
|
||||
Rand::init(getTime());
|
||||
TableData::init();
|
||||
|
||||
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
|
||||
|
||||
PlayerManager::init();
|
||||
PlayerMovement::init();
|
||||
BuiltinCommands::init();
|
||||
@@ -132,9 +135,10 @@ int main() {
|
||||
Email::init();
|
||||
Groups::init();
|
||||
Racing::init();
|
||||
Database::open();
|
||||
Trading::init();
|
||||
|
||||
Database::open();
|
||||
|
||||
switch (settings::EVENTMODE) {
|
||||
case 0: break; // no event
|
||||
case 1: std::cout << "[INFO] Event active. Hey, Hey It's Knishmas!" << std::endl; break;
|
||||
@@ -197,21 +201,21 @@ size_t U8toU16(std::string src, char16_t* des, size_t max) {
|
||||
return tmp.length();
|
||||
}
|
||||
|
||||
time_t getTime() {
|
||||
int64_t getTime() {
|
||||
using namespace std::chrono;
|
||||
|
||||
milliseconds value = duration_cast<milliseconds>((time_point_cast<milliseconds>(steady_clock::now())).time_since_epoch());
|
||||
|
||||
return (time_t)value.count();
|
||||
return (int64_t)value.count();
|
||||
}
|
||||
|
||||
// returns system time in seconds
|
||||
time_t getTimestamp() {
|
||||
int64_t getTimestamp() {
|
||||
using namespace std::chrono;
|
||||
|
||||
seconds value = duration_cast<seconds>((time_point_cast<seconds>(system_clock::now())).time_since_epoch());
|
||||
|
||||
return (time_t)value.count();
|
||||
return (int64_t)value.count();
|
||||
}
|
||||
|
||||
// convert integer timestamp (in s) to FF systime struct
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
|
||||
|
||||
CNLoginServer::CNLoginServer(uint16_t p) {
|
||||
serverType = "login";
|
||||
port = p;
|
||||
pHandler = &CNLoginServer::handlePacket;
|
||||
init();
|
||||
@@ -208,9 +209,12 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
|
||||
// send the resp in with original key
|
||||
sock->sendPacket(resp, P_LS2CL_REP_LOGIN_SUCC);
|
||||
|
||||
uint64_t defaultKey;
|
||||
memcpy(&defaultKey, CNSocketEncryption::defaultKey, sizeof(defaultKey));
|
||||
|
||||
// update keys
|
||||
sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1));
|
||||
sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1));
|
||||
sock->setFEKey(CNSocketEncryption::createNewKey(defaultKey, login->iClientVerC, 1));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "Login Server: Login success. Welcome " << userLogin << " [" << loginSessions[sock].userID << "]" << std::endl;
|
||||
@@ -570,8 +574,8 @@ void CNLoginServer::killConnection(CNSocket* cns) {
|
||||
}
|
||||
|
||||
void CNLoginServer::onStep() {
|
||||
time_t currTime = getTime();
|
||||
static time_t lastCheck = 0;
|
||||
int64_t currTime = getTime();
|
||||
static int64_t lastCheck = 0;
|
||||
|
||||
if (currTime - lastCheck < 16000)
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
struct CNLoginData {
|
||||
int userID;
|
||||
time_t lastHeartbeat;
|
||||
int64_t lastHeartbeat;
|
||||
};
|
||||
|
||||
enum class LoginError {
|
||||
|
||||
@@ -16,6 +16,7 @@ std::map<uint32_t, PacketHandler> CNShardServer::ShardPackets;
|
||||
std::list<TimerEvent> CNShardServer::Timers;
|
||||
|
||||
CNShardServer::CNShardServer(uint16_t p) {
|
||||
serverType = "shard";
|
||||
port = p;
|
||||
pHandler = &CNShardServer::handlePacket;
|
||||
REGISTER_SHARD_TIMER(keepAliveTimer, 4000);
|
||||
@@ -58,7 +59,7 @@ void CNShardServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
||||
PlayerManager::players[sock]->lastHeartbeat = getTime();
|
||||
}
|
||||
|
||||
void CNShardServer::keepAliveTimer(CNServer* serv, time_t currTime) {
|
||||
void CNShardServer::keepAliveTimer(CNServer* serv, int64_t currTime) {
|
||||
for (auto& pair : PlayerManager::players) {
|
||||
if (pair.second->lastHeartbeat != 0 && currTime - pair.second->lastHeartbeat > settings::TIMEOUT) {
|
||||
// if the client hasn't responded in 60 seconds, its a dead connection so throw it out
|
||||
@@ -71,7 +72,7 @@ void CNShardServer::keepAliveTimer(CNServer* serv, time_t currTime) {
|
||||
}
|
||||
}
|
||||
|
||||
void CNShardServer::periodicSaveTimer(CNServer* serv, time_t currTime) {
|
||||
void CNShardServer::periodicSaveTimer(CNServer* serv, int64_t currTime) {
|
||||
if (PlayerManager::players.empty())
|
||||
return;
|
||||
|
||||
@@ -113,7 +114,7 @@ void CNShardServer::kill() {
|
||||
}
|
||||
|
||||
void CNShardServer::onStep() {
|
||||
time_t currTime = getTime();
|
||||
int64_t currTime = getTime();
|
||||
|
||||
// do not evaluate timers if the server is shutting down
|
||||
if (!active)
|
||||
|
||||
@@ -11,8 +11,8 @@ class CNShardServer : public CNServer {
|
||||
private:
|
||||
static void handlePacket(CNSocket* sock, CNPacketData* data);
|
||||
|
||||
static void keepAliveTimer(CNServer*, time_t);
|
||||
static void periodicSaveTimer(CNServer* serv, time_t currTime);
|
||||
static void keepAliveTimer(CNServer*, int64_t);
|
||||
static void periodicSaveTimer(CNServer* serv, int64_t currTime);
|
||||
|
||||
public:
|
||||
static std::map<uint32_t, PacketHandler> ShardPackets;
|
||||
|
||||
@@ -72,7 +72,7 @@ static int process_email(char *buff, std::string email) {
|
||||
return i;
|
||||
}
|
||||
|
||||
static void tick(CNServer *serv, time_t delta) {
|
||||
static void tick(CNServer *serv, int64_t delta) {
|
||||
std::lock_guard<std::mutex> lock(sockLock);
|
||||
char buff[BUFSIZE];
|
||||
int n;
|
||||
|
||||
@@ -17,7 +17,7 @@ int settings::DBSAVEINTERVAL = 240;
|
||||
int settings::SHARDPORT = 23001;
|
||||
std::string settings::SHARDSERVERIP = "127.0.0.1";
|
||||
bool settings::LOCALHOSTWORKAROUND = true;
|
||||
time_t settings::TIMEOUT = 60000;
|
||||
int64_t settings::TIMEOUT = 60000;
|
||||
int settings::VIEWDISTANCE = 25600;
|
||||
bool settings::SIMULATEMOBS = true;
|
||||
bool settings::ANTICHEAT = true;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace settings {
|
||||
extern std::string SHARDSERVERIP;
|
||||
extern bool LOCALHOSTWORKAROUND;
|
||||
extern bool ANTICHEAT;
|
||||
extern time_t TIMEOUT;
|
||||
extern int64_t TIMEOUT;
|
||||
extern int VIEWDISTANCE;
|
||||
extern bool SIMULATEMOBS;
|
||||
extern int SPAWN_X;
|
||||
|
||||
3
vendor/bcrypt/bcrypt.c
vendored
3
vendor/bcrypt/bcrypt.c
vendored
@@ -24,7 +24,6 @@
|
||||
|
||||
#ifdef _WIN32 || _WIN64
|
||||
// On windows we need to generate random bytes differently.
|
||||
typedef __int64 ssize_t;
|
||||
#define BCRYPT_HASHSIZE 60
|
||||
|
||||
#include "bcrypt.h"
|
||||
@@ -51,6 +50,7 @@ static int try_close(int fd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static int try_read(int fd, char *out, size_t count)
|
||||
{
|
||||
size_t total;
|
||||
@@ -75,6 +75,7 @@ static int try_read(int fd, char *out, size_t count)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a best effort implementation. Nothing prevents a compiler from
|
||||
|
||||
Reference in New Issue
Block a user