2 Commits

Author SHA1 Message Date
CakeLancelot
ab480d88f1 Update version numbers to 1.5 2023-10-07 18:20:04 -05:00
CakeLancelot
89772d763b CI: specify Ubuntu runner version and fix artifact zip name
We were technically already using 22.04 for a bit, it got updated without us noticing since the version was set to `ubuntu-latest`.
Affix the version to 22.04 so that it doesn't get unexpectedly updated again, and update the artifact's zip to reflect the change.
2023-10-07 17:13:13 -05:00
29 changed files with 73 additions and 74 deletions

View File

@@ -53,7 +53,7 @@ jobs:
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: 'ubuntu20_04-bin-x64-${{ env.SHORT_SHA }}'
name: 'ubuntu22_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-latest
runs-on: ubuntu-22.04
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.4/OpenFusionClient-1.4-Installer.exe) - choose to run the file.
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.
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.4/OpenFusionClient-1.4.zip).
1. Download the client from [here](https://github.com/OpenFusionProject/OpenFusion/releases/download/1.5/OpenFusionClient-1.5.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.4-original.zip` or `OpenFusionServer-1.4-academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.4).
1. Grab `OpenFusionServer-1.5-original.zip` or `OpenFusionServer-1.5-academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.5).
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);
int64_t until = getTime() + (int64_t)duration * 100;
time_t until = getTime() + (time_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);
int64_t currTime = getTime();
time_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, int64_t currTime) {
void Combat::npcAttackPc(Mob *mob, time_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
int64_t currTime = getTime();
time_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, int64_t currTime) {
static int64_t lastHealTime = 0;
static void playerTick(CNServer *serv, time_t currTime) {
static time_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, int64_t currTime);
void npcAttackPc(Mob *mob, time_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>, int64_t> Eggs::EggBuffs;
std::map<std::pair<CNSocket*, int32_t>, time_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
int64_t until = getTime() + (int64_t)duration * 1000;
time_t until = getTime() + (time_t)duration * 1000;
EggBuffs[key] = until;
return 0;
}
static void eggStep(CNServer* serv, int64_t currTime) {
static void eggStep(CNServer* serv, time_t currTime) {
// tick buffs
int64_t timeStamp = currTime;
time_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() + (int64_t)type->regen * 1000;
egg->deadUntil = getTime() + (time_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>, int64_t> EggBuffs;
extern std::map<std::pair<CNSocket*, int32_t>, time_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*, int64_t) = nullptr;
void (*_stepAI)(CombatNPC*, time_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(int64_t currTime) {
virtual void stepAI(time_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;
int64_t deadUntil;
time_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);
int64_t until = getTime() + (int64_t)Nanos::SkillTable[144].durationTime[0] * 100;
time_t until = getTime() + (time_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, int64_t currTime);
static void roamingStep(Mob *mob, time_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, int64_t currTime) {
bool MobAI::aggroCheck(Mob *mob, time_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, int64_t currTime) {
static void useAbilities(Mob *mob, time_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, int64_t currTime) {
static void deadStep(Mob *mob, time_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, int64_t currTime) {
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW));
}
static void combatStep(Mob *mob, int64_t currTime) {
static void combatStep(Mob *mob, time_t currTime) {
assert(mob->target != nullptr);
// lose aggro if the player lost connection
@@ -542,7 +542,7 @@ static void combatStep(Mob *mob, int64_t currTime) {
return;
// unbuffing
std::unordered_map<int32_t, int64_t>::iterator it = mob->unbuffTimes.begin();
std::unordered_map<int32_t, time_t>::iterator it = mob->unbuffTimes.begin();
while (it != mob->unbuffTimes.end()) {
if (currTime >= it->second) {
@@ -640,7 +640,7 @@ static void combatStep(Mob *mob, int64_t currTime) {
}
}
void MobAI::incNextMovement(Mob *mob, int64_t currTime) {
void MobAI::incNextMovement(Mob *mob, time_t currTime) {
if (currTime == 0)
currTime = getTime();
@@ -648,7 +648,7 @@ void MobAI::incNextMovement(Mob *mob, int64_t currTime) {
mob->nextMovement = currTime + delay/2 + Rand::rand(delay/2);
}
static void roamingStep(Mob *mob, int64_t currTime) {
static void roamingStep(Mob *mob, time_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, int64_t currTime) {
}
}
static void retreatStep(Mob *mob, int64_t currTime) {
static void retreatStep(Mob *mob, time_t currTime) {
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
return;
@@ -781,7 +781,7 @@ static void retreatStep(Mob *mob, int64_t currTime) {
}
}
void MobAI::step(CombatNPC *npc, int64_t currTime) {
void MobAI::step(CombatNPC *npc, time_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*, int64_t);
void step(CombatNPC*, time_t);
};
struct Mob : public CombatNPC {
// general
MobState state = MobState::INACTIVE;
std::unordered_map<int32_t,int64_t> unbuffTimes = {};
std::unordered_map<int32_t,time_t> unbuffTimes = {};
// dead
int64_t killedTime = 0;
int64_t regenTime = 0;
time_t killedTime = 0;
time_t regenTime = 0;
bool summoned = false;
bool despawned = false; // for the sake of death animations
// roaming
int idleRange = 0;
const int sightRange = 0;
int64_t nextMovement = 0;
time_t nextMovement = 0;
bool staticPath = false;
int roamX = 0, roamY = 0, roamZ = 0;
// combat
CNSocket *target = nullptr;
int64_t nextAttack = 0;
int64_t lastDrainTime = 0;
time_t nextAttack = 0;
time_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, int64_t currTime=0);
bool aggroCheck(Mob *mob, int64_t currTime);
void incNextMovement(Mob *mob, time_t currTime=0);
bool aggroCheck(Mob *mob, time_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, int64_t currTime) {
static void step(CNServer *serv, time_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] = {};
int64_t lastHeartbeat = 0;
time_t lastHeartbeat = 0;
int suspicionRating = 0;
int64_t lastShot = 0;
time_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;
int64_t startTime;
time_t startTime;
};
namespace Racing {

View File

@@ -319,7 +319,7 @@ static void stepNPCPathing() {
}
}
static void tickTransportationSystem(CNServer* serv, int64_t currTime) {
static void tickTransportationSystem(CNServer* serv, time_t currTime) {
stepNPCPathing();
stepSkywaySystem();
}

View File

@@ -208,15 +208,15 @@ public:
};
class CNServer;
typedef void (*TimerHandler)(CNServer* serv, int64_t time);
typedef void (*TimerHandler)(CNServer* serv, time_t time);
// timer struct
struct TimerEvent {
TimerHandler handlr;
int64_t delta; // time to be added to the current time on reset
int64_t scheduledEvent; // time to call handlr()
time_t delta; // time to be added to the current time on reset
time_t scheduledEvent; // time to call handlr()
TimerEvent(TimerHandler h, int64_t d): handlr(h), delta(d) {
TimerEvent(TimerHandler h, time_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, int64_t currTime) {
void CNShared::pruneLoginMetadata(CNServer *serv, time_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;
int64_t timestamp;
time_t timestamp;
};
namespace CNShared {
void storeLoginMetadata(int64_t sk, LoginMetadata *lm);
LoginMetadata* getLoginMetadata(int64_t sk);
void pruneLoginMetadata(CNServer *serv, int64_t currTime);
void pruneLoginMetadata(CNServer *serv, time_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
int64_t getTime();
int64_t getTimestamp();
time_t getTime();
time_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;
int64_t BannedUntil;
time_t BannedUntil;
std::string BanReason;
};

View File

@@ -64,7 +64,7 @@ void terminate(int arg) {
}
#ifdef _WIN32
static BOOL WINAPI winTerminate(DWORD arg) {
static BOOL 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();
}
int64_t getTime() {
time_t getTime() {
using namespace std::chrono;
milliseconds value = duration_cast<milliseconds>((time_point_cast<milliseconds>(steady_clock::now())).time_since_epoch());
return (int64_t)value.count();
return (time_t)value.count();
}
// returns system time in seconds
int64_t getTimestamp() {
time_t getTimestamp() {
using namespace std::chrono;
seconds value = duration_cast<seconds>((time_point_cast<seconds>(system_clock::now())).time_since_epoch());
return (int64_t)value.count();
return (time_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() {
int64_t currTime = getTime();
static int64_t lastCheck = 0;
time_t currTime = getTime();
static time_t lastCheck = 0;
if (currTime - lastCheck < 16000)
return;

View File

@@ -7,7 +7,7 @@
struct CNLoginData {
int userID;
int64_t lastHeartbeat;
time_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, int64_t currTime) {
void CNShardServer::keepAliveTimer(CNServer* serv, time_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, int64_t currTime) {
}
}
void CNShardServer::periodicSaveTimer(CNServer* serv, int64_t currTime) {
void CNShardServer::periodicSaveTimer(CNServer* serv, time_t currTime) {
if (PlayerManager::players.empty())
return;
@@ -114,7 +114,7 @@ void CNShardServer::kill() {
}
void CNShardServer::onStep() {
int64_t currTime = getTime();
time_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*, int64_t);
static void periodicSaveTimer(CNServer* serv, int64_t currTime);
static void keepAliveTimer(CNServer*, time_t);
static void periodicSaveTimer(CNServer* serv, time_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, int64_t delta) {
static void tick(CNServer *serv, time_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;
int64_t settings::TIMEOUT = 60000;
time_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 int64_t TIMEOUT;
extern time_t TIMEOUT;
extern int VIEWDISTANCE;
extern bool SIMULATEMOBS;
extern int SPAWN_X;

View File

@@ -24,6 +24,7 @@
#ifdef _WIN32 || _WIN64
// On windows we need to generate random bytes differently.
typedef __int64 ssize_t;
#define BCRYPT_HASHSIZE 60
#include "bcrypt.h"
@@ -50,7 +51,6 @@ 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,7 +75,6 @@ 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