4 Commits

Author SHA1 Message Date
630e7a69b1 Make the bcrypt.c changes portable 2023-09-13 23:19:24 +02:00
eb72dc5f2e Forgot to commit this one 2023-09-13 23:08:09 +02:00
a7993bfc10 Replace all instances of time_t with int64_t
This solves all of our problems with 32-bit time_t on 32-bit Windows.
Only had to revert a single occurance in timeStampToStruct() in
src/main.cpp to match the args of localtime().

Invocation:
find src/ -type f | xargs sed -i 's/time_t/int64_t/g'
2023-09-13 22:15:14 +02:00
a716883971 Builds and runs mostly fine 2023-09-13 21:57:19 +02:00
29 changed files with 74 additions and 73 deletions

View File

@@ -53,7 +53,7 @@ jobs:
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: 'ubuntu22_04-bin-x64-${{ env.SHORT_SHA }}'
name: 'ubuntu20_04-bin-x64-${{ env.SHORT_SHA }}'
path: bin
windows-build:
@@ -112,7 +112,7 @@ jobs:
copy-artifacts:
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: [windows-build, ubuntu-build]
env:
BOT_SSH_KEY: ${{ secrets.BOT_SSH_KEY }}

View File

@@ -13,13 +13,13 @@ OpenFusion is a reverse-engineered server for FusionFall. It primarily targets v
### Getting Started
#### Method A: Installer (Easiest)
1. Download the client installer by clicking [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.5/OpenFusionClient-1.5-Installer.exe) - choose to run the file.
1. Download the client installer by clicking [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.4/OpenFusionClient-1.4-Installer.exe) - choose to run the file.
2. After a few moments, the client should open: you will be given a choice between two public servers by default. Select the one you wish to play and click connect.
3. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen.
4. Make a new character, and enjoy the game! Your progress will be saved automatically, and you can resume playing by entering the login details you used in step 3.
#### Method B: Standalone .zip file
1. Download the client from [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.5/OpenFusionClient-1.5.zip).
1. Download the client from [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.4/OpenFusionClient-1.4.zip).
2. Extract it to a folder of your choice. Note: if you are upgrading from an older version, it is preferable to start with a fresh folder rather than overwriting a previous install.
3. Run OpenFusionClient.exe - you will be given a choice between two public servers by default. Select the one you wish to play and click connect.
4. To create an account, simply enter the details you wish to use at the login screen then click Log In. Do *not* click register, as this will just lead to a blank screen.
@@ -29,7 +29,7 @@ Instructions for getting the client to run on Linux through Wine can be found [h
### Hosting a server
1. Grab `OpenFusionServer-1.5-original.zip` or `OpenFusionServer-1.5-academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.5).
1. Grab `OpenFusionServer-1.4-original.zip` or `OpenFusionServer-1.4-academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.4).
2. Extract it to a folder of your choice, then run `winfusion.exe` (Windows) or `fusion` (Linux) to start the server.
3. Add a new server to the client's list:
1. For Description, enter anything you want. This is what will show up in the server list.

View File

@@ -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;

View File

@@ -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
@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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; }

View File

@@ -11,7 +11,7 @@ struct EPInfo {
struct EPRace {
std::set<int> collectedRings;
int mode, ticketSlot;
time_t startTime;
int64_t startTime;
};
namespace Racing {

View File

@@ -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();
}

View File

@@ -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;
}
};

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -13,7 +13,7 @@ namespace Database {
int AccountID;
std::string Password;
int Selected;
time_t BannedUntil;
int64_t BannedUntil;
std::string BanReason;
};

View File

@@ -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;
}
@@ -201,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

View File

@@ -574,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;

View File

@@ -7,7 +7,7 @@
struct CNLoginData {
int userID;
time_t lastHeartbeat;
int64_t lastHeartbeat;
};
enum class LoginError {

View File

@@ -59,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
@@ -72,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;
@@ -114,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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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