mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-26 15:00:06 +00:00
Compare commits
56 Commits
cd908666af
...
bbaaa53df2
Author | SHA1 | Date | |
---|---|---|---|
|
bbaaa53df2 | ||
|
273b73379e | ||
|
1709d2b0ef | ||
|
457c320d48 | ||
|
29024e9351 | ||
|
0994aaff71 | ||
|
cb94018e46 | ||
|
e0a0cc186e | ||
|
d4253c3b49 | ||
|
5b58055924 | ||
|
7a7cdb1330 | ||
|
0e054e21b6 | ||
|
ad9bf2a9e3 | ||
|
a38bf0e7be | ||
|
3d22103113 | ||
|
761581afd7 | ||
|
daec7a33b7 | ||
|
462ced6685 | ||
|
cc190efc63 | ||
|
df1b4ff160 | ||
|
9f7c8a18df | ||
|
ea61ed1aaa | ||
|
9982d9bdda | ||
|
398e9fddf8 | ||
|
fd664c4f19 | ||
|
feaaf4cab9 | ||
|
d3e6bae5d9 | ||
|
0f042c4233 | ||
|
3dc7124f58 | ||
|
71e78afa0b | ||
|
0a2b3fbdad | ||
|
911dbaab83 | ||
|
7183180be5 | ||
|
3f1f78942e | ||
|
a9dea36882 | ||
|
de23779209 | ||
|
748a82e223 | ||
|
231e88fd55 | ||
|
abfb562489 | ||
|
589c5a8732 | ||
|
0b8b92b7f6 | ||
|
f0cf6326e5 | ||
|
82bc94c01c | ||
|
5d9dcb8609 | ||
|
166e148878 | ||
|
14b02ec5d2 | ||
d50c312227 | |||
18ed96493f | |||
71a7d3d164 | |||
520efd6dd5 | |||
|
0a8ef2ebbd | ||
|
3f4aca8a5d | ||
|
b0de75d80e | ||
|
8f88edaad1 | ||
|
6896d35427 | ||
|
d74a9747d9 |
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"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}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -45,7 +45,7 @@ add_executable(openfusion ${SOURCES})
|
|||||||
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
|
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
|
||||||
|
|
||||||
# find sqlite3 and use it
|
# find sqlite3 and use it
|
||||||
find_package(SQLite3 REQUIRED)
|
find_package(sqlite3 REQUIRED)
|
||||||
target_include_directories(openfusion PRIVATE ${SQLite3_INCLUDE_DIRS})
|
target_include_directories(openfusion PRIVATE ${SQLite3_INCLUDE_DIRS})
|
||||||
target_link_libraries(openfusion PRIVATE ${SQLite3_LIBRARIES})
|
target_link_libraries(openfusion PRIVATE ${SQLite3_LIBRARIES})
|
||||||
|
|
||||||
@ -57,5 +57,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...
|
# 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")
|
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)
|
find_package(Threads REQUIRED)
|
||||||
target_link_libraries(openfusion PRIVATE pthread)
|
target_link_libraries(openfusion pthread)
|
||||||
endif()
|
endif()
|
||||||
|
21
Dockerfile
21
Dockerfile
@ -1,21 +0,0 @@
|
|||||||
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
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020-2023 OpenFusion Contributors
|
Copyright (c) 2020-2022 OpenFusion Contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -25,8 +25,6 @@ OpenFusion is a reverse-engineered server for FusionFall. It primarily targets v
|
|||||||
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.
|
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.
|
||||||
5. 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 4.
|
5. 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 4.
|
||||||
|
|
||||||
Instructions for getting the client to run on Linux through Wine can be found [here](https://github.com/OpenFusionProject/OpenFusion/wiki/Running-the-game-client-on-Linux).
|
|
||||||
|
|
||||||
### Hosting a server
|
### 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.4-original.zip` or `OpenFusionServer-1.4-academy.zip` from [here](https://github.com/OpenFusionProject/OpenFusion/releases/tag/1.4).
|
||||||
@ -37,7 +35,7 @@ Instructions for getting the client to run on Linux through Wine can be found [h
|
|||||||
3. Lastly Game Version - select `beta-20100104` if you downloaded the original zip, or `beta-20111013` if you downloaded the academy zip.
|
3. Lastly Game Version - select `beta-20100104` if you downloaded the original zip, or `beta-20111013` if you downloaded the academy zip.
|
||||||
5. Once you've added the server to the list, connect to it and log in. If you're having trouble with this, refer to steps 4 and 5 from the previous section.
|
5. Once you've added the server to the list, connect to it and log in. If you're having trouble with this, refer to steps 4 and 5 from the previous section.
|
||||||
|
|
||||||
If you want to run the latest development builds of the server, [compiled binaries (artifacts) for each functional commit can be found here.](http://cdn.dexlabs.systems/of-builds/)
|
If you want, [compiled binaries (artifacts) for each functional commit can be found here.](http://cdn.dexlabs.systems/of-builds/)
|
||||||
|
|
||||||
For a more detailed overview of the game's architecture and how to configure it, read the following sections.
|
For a more detailed overview of the game's architecture and how to configure it, read the following sections.
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ This just works if you're all under the same LAN, but if you want to play over t
|
|||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
You have two choices for compiling OpenFusion: the included Makefile and the included CMakeLists file.
|
You have two choices for compiling OpenFusion: the included Makefile and the included CMakeLists file.
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
version: '3.4'
|
|
||||||
|
|
||||||
services:
|
|
||||||
openfusion:
|
|
||||||
image: openfusion
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
ports:
|
|
||||||
- "23000:23000"
|
|
||||||
- "23001:23001"
|
|
||||||
- "8003:8003"
|
|
@ -270,30 +270,10 @@ void Abilities::useNPCSkill(EntityRef npc, int skillID, std::vector<ICombatant*>
|
|||||||
NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen);
|
NPCManager::sendToViewable(entity, pkt, P_FE2CL_NPC_SKILL_HIT, resplen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<ICombatant*> entityRefsToCombatants(std::vector<EntityRef> refs) {
|
std::vector<ICombatant*> Abilities::matchTargets(SkillData* skill, int count, int32_t *ids) {
|
||||||
std::vector<ICombatant*> combatants;
|
|
||||||
for(EntityRef ref : refs) {
|
|
||||||
if(ref.kind == EntityKind::PLAYER)
|
|
||||||
combatants.push_back(dynamic_cast<ICombatant*>(PlayerManager::getPlayer(ref.sock)));
|
|
||||||
else if(ref.kind == EntityKind::COMBAT_NPC || ref.kind == EntityKind::MOB)
|
|
||||||
combatants.push_back(dynamic_cast<ICombatant*>(ref.getEntity()));
|
|
||||||
}
|
|
||||||
return combatants;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ICombatant*> Abilities::matchTargets(ICombatant* src, SkillData* skill, int count, int32_t *ids) {
|
|
||||||
|
|
||||||
if(skill->targetType == SkillTargetType::GROUP) {
|
|
||||||
// group
|
|
||||||
if(count != 1 || ids[0] != src->getID()) {
|
|
||||||
std::cout << "[WARN] skill: bad group targeting (id " << ids[0] << ")\n";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return entityRefsToCombatants(src->getGroupMembers());
|
|
||||||
}
|
|
||||||
|
|
||||||
// individuals
|
|
||||||
std::vector<ICombatant*> targets;
|
std::vector<ICombatant*> targets;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int32_t id = ids[i];
|
int32_t id = ids[i];
|
||||||
if (skill->targetType == SkillTargetType::MOBS) {
|
if (skill->targetType == SkillTargetType::MOBS) {
|
||||||
@ -306,8 +286,8 @@ std::vector<ICombatant*> Abilities::matchTargets(ICombatant* src, SkillData* ski
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cout << "[WARN] skill: invalid mob target (id " << id << ")\n";
|
std::cout << "[WARN] skill: invalid mob target (id " << id << ")\n";
|
||||||
} else if(skill->targetType == SkillTargetType::PLAYERS) {
|
} else if(skill->targetType == SkillTargetType::SELF || skill->targetType == SkillTargetType::GROUP) {
|
||||||
// player
|
// players (?)
|
||||||
Player* plr = PlayerManager::getPlayerFromID(id);
|
Player* plr = PlayerManager::getPlayerFromID(id);
|
||||||
if (plr != nullptr) {
|
if (plr != nullptr) {
|
||||||
targets.push_back(dynamic_cast<ICombatant*>(plr));
|
targets.push_back(dynamic_cast<ICombatant*>(plr));
|
||||||
|
@ -21,7 +21,7 @@ enum class SkillEffectTarget {
|
|||||||
|
|
||||||
enum class SkillTargetType {
|
enum class SkillTargetType {
|
||||||
MOBS = 1,
|
MOBS = 1,
|
||||||
PLAYERS = 2,
|
SELF = 2,
|
||||||
GROUP = 3
|
GROUP = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,6 +63,6 @@ namespace Abilities {
|
|||||||
void useNanoSkill(CNSocket*, SkillData*, sNano&, std::vector<ICombatant*>);
|
void useNanoSkill(CNSocket*, SkillData*, sNano&, std::vector<ICombatant*>);
|
||||||
void useNPCSkill(EntityRef, int skillID, std::vector<ICombatant*>);
|
void useNPCSkill(EntityRef, int skillID, std::vector<ICombatant*>);
|
||||||
|
|
||||||
std::vector<ICombatant*> matchTargets(ICombatant*, SkillData*, int, int32_t*);
|
std::vector<ICombatant*> matchTargets(SkillData*, int, int32_t*);
|
||||||
int getCSTBFromST(int eSkillType);
|
int getCSTBFromST(int eSkillType);
|
||||||
}
|
}
|
||||||
|
@ -304,40 +304,11 @@ static std::pair<int,int> getDamage(int attackPower, int defensePower, bool shou
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool checkRapidFire(CNSocket *sock, int targetCount) {
|
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
|
||||||
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)
|
|
||||||
plr->suspicionRating += plr->fireRate * 100 + plr->lastShot - currTime; // lose suspicion for delayed firing
|
|
||||||
|
|
||||||
plr->lastShot = currTime;
|
|
||||||
|
|
||||||
// 3+ targets should never be possible
|
|
||||||
if (targetCount > 3)
|
|
||||||
plr->suspicionRating += 10001;
|
|
||||||
|
|
||||||
// kill the socket when the player is too suspicious
|
|
||||||
if (plr->suspicionRating > 10000) {
|
|
||||||
sock->kill();
|
|
||||||
CNShardServer::_killConnection(sock);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
static void pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
||||||
auto pkt = (sP_CL2FE_REQ_PC_ATTACK_NPCs*)data->buf;
|
auto pkt = (sP_CL2FE_REQ_PC_ATTACK_NPCs*)data->buf;
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
auto targets = (int32_t*)data->trailers;
|
auto targets = (int32_t*)data->trailers;
|
||||||
|
|
||||||
// kick the player if firing too rapidly
|
|
||||||
if (settings::ANTICHEAT && checkRapidFire(sock, pkt->iNPCCnt))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IMPORTANT: This validates memory safety in addition to preventing
|
* IMPORTANT: This validates memory safety in addition to preventing
|
||||||
* ordinary cheating. If the client sends a very large number of trailing
|
* ordinary cheating. If the client sends a very large number of trailing
|
||||||
|
@ -29,7 +29,7 @@ void Eggs::eggBuffPlayer(CNSocket* sock, int skillId, int eggId, int duration) {
|
|||||||
SkillData* skill = &Abilities::SkillTable[skillId];
|
SkillData* skill = &Abilities::SkillTable[skillId];
|
||||||
if(skill->drainType == SkillDrainType::PASSIVE) {
|
if(skill->drainType == SkillDrainType::PASSIVE) {
|
||||||
// apply buff
|
// apply buff
|
||||||
if(skill->targetType != SkillTargetType::PLAYERS) {
|
if(skill->targetType != SkillTargetType::SELF) {
|
||||||
std::cout << "[WARN] weird skill type for egg " << eggId << " with skill " << skillId << ", should be " << (int)skill->targetType << std::endl;
|
std::cout << "[WARN] weird skill type for egg " << eggId << " with skill " << skillId << ", should be " << (int)skill->targetType << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ static void emailReceiveItemSingle(CNSocket* sock, CNPacketData* data) {
|
|||||||
auto pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf;
|
auto pkt = (sP_CL2FE_REQ_PC_RECV_EMAIL_ITEM*)data->buf;
|
||||||
Player* plr = PlayerManager::getPlayer(sock);
|
Player* plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iEmailItemSlot < 1 || pkt->iEmailItemSlot > 4)
|
if (pkt->iSlotNum < 0 || pkt->iSlotNum >= AINVEN_COUNT || pkt->iSlotNum < 1 || pkt->iSlotNum > 4)
|
||||||
return; // sanity check
|
return; // sanity check
|
||||||
|
|
||||||
// get email item from db and delete it
|
// get email item from db and delete it
|
||||||
@ -252,26 +252,11 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
|
|||||||
if (attachment.ItemInven.iID == 0)
|
if (attachment.ItemInven.iID == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sItemBase* item = &pkt->aItem[i].ItemInven;
|
|
||||||
sItemBase* real = &plr->Inven[attachment.iSlotNum];
|
|
||||||
|
|
||||||
resp.aItem[i] = attachment;
|
resp.aItem[i] = attachment;
|
||||||
attachments.push_back(attachment.ItemInven);
|
attachments.push_back(attachment.ItemInven);
|
||||||
attSlots.push_back(attachment.iSlotNum);
|
attSlots.push_back(attachment.iSlotNum);
|
||||||
if (real->iOpt <= item->iOpt) // delete item (if they attached the whole stack)
|
// delete item
|
||||||
*real = { 0, 0, 0, 0 };
|
plr->Inven[attachment.iSlotNum] = { 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
|
int cost = pkt->iCash + 50 + 20 * attachments.size(); // attached taros + postage
|
||||||
@ -291,7 +276,7 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
|
|||||||
0 // DeleteTime (unimplemented)
|
0 // DeleteTime (unimplemented)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Database::sendEmail(&email, attachments, plr)) {
|
if (!Database::sendEmail(&email, attachments)) {
|
||||||
plr->money += cost; // give money back
|
plr->money += cost; // give money back
|
||||||
// give items back
|
// give items back
|
||||||
while (!attachments.empty()) {
|
while (!attachments.empty()) {
|
||||||
|
@ -73,6 +73,16 @@ std::vector<ICombatant*> Nanos::applyNanoBuff(SkillData* skill, Player* plr) {
|
|||||||
assert(skill->drainType == SkillDrainType::PASSIVE);
|
assert(skill->drainType == SkillDrainType::PASSIVE);
|
||||||
|
|
||||||
EntityRef self = PlayerManager::getSockFromID(plr->iID);
|
EntityRef self = PlayerManager::getSockFromID(plr->iID);
|
||||||
|
std::vector<ICombatant*> affected;
|
||||||
|
std::vector<EntityRef> targets;
|
||||||
|
if (skill->targetType == SkillTargetType::GROUP) {
|
||||||
|
targets = plr->getGroupMembers(); // group
|
||||||
|
}
|
||||||
|
else if(skill->targetType == SkillTargetType::SELF) {
|
||||||
|
targets.push_back(self); // self
|
||||||
|
} else {
|
||||||
|
std::cout << "[WARN] Passive skill with type " << skill->skillType << " has target type MOB" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
|
int timeBuffId = Abilities::getCSTBFromST(skill->skillType);
|
||||||
int boost = Nanos::getNanoBoost(plr) ? 3 : 0;
|
int boost = Nanos::getNanoBoost(plr) ? 3 : 0;
|
||||||
@ -85,22 +95,21 @@ std::vector<ICombatant*> Nanos::applyNanoBuff(SkillData* skill, Player* plr) {
|
|||||||
BuffClass::NONE, // overwritten per target
|
BuffClass::NONE, // overwritten per target
|
||||||
};
|
};
|
||||||
|
|
||||||
// for passive skills, using just the player as a target is fine
|
for (EntityRef target : targets) {
|
||||||
// this is because the group skill type will ignore the count,
|
Entity* entity = target.getEntity();
|
||||||
// and the other option is single-target
|
if (entity->kind != PLAYER && entity->kind != COMBAT_NPC && entity->kind != MOB)
|
||||||
std::vector<ICombatant*> targets = Abilities::matchTargets(dynamic_cast<ICombatant*>(plr), skill, 1, &plr->iID);
|
continue; // not a combatant
|
||||||
std::vector<ICombatant*> affected;
|
|
||||||
for (ICombatant* target : targets) {
|
|
||||||
|
|
||||||
passiveBuff.buffStackClass = target == plr ? BuffClass::NANO : BuffClass::GROUP_NANO;
|
passiveBuff.buffStackClass = target == self ? BuffClass::NANO : BuffClass::GROUP_NANO;
|
||||||
if(target->addBuff(timeBuffId,
|
ICombatant* combatant = dynamic_cast<ICombatant*>(entity);
|
||||||
|
if(combatant->addBuff(timeBuffId,
|
||||||
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
|
[](EntityRef self, Buff* buff, int status, BuffStack* stack) {
|
||||||
Buffs::timeBuffUpdate(self, buff, status, stack);
|
Buffs::timeBuffUpdate(self, buff, status, stack);
|
||||||
},
|
},
|
||||||
[](EntityRef self, Buff* buff, time_t currTime) {
|
[](EntityRef self, Buff* buff, time_t currTime) {
|
||||||
// no-op
|
// no-op
|
||||||
},
|
},
|
||||||
&passiveBuff)) affected.push_back(target);
|
&passiveBuff)) affected.push_back(combatant);
|
||||||
}
|
}
|
||||||
|
|
||||||
return affected;
|
return affected;
|
||||||
@ -308,8 +317,8 @@ static void nanoSkillUseHandler(CNSocket* sock, CNPacketData* data) {
|
|||||||
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
std::cout << PlayerManager::getPlayerName(plr) << " requested to summon nano skill " << std::endl;
|
||||||
)
|
)
|
||||||
|
|
||||||
ICombatant* plrCombatant = dynamic_cast<ICombatant*>(plr);
|
// TODO ABILITIES
|
||||||
std::vector<ICombatant*> targetData = Abilities::matchTargets(plrCombatant, skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1));
|
std::vector<ICombatant*> targetData = Abilities::matchTargets(skillData, pkt->iTargetCnt, (int32_t*)(pkt + 1));
|
||||||
Abilities::useNanoSkill(sock, skillData, nano, targetData);
|
Abilities::useNanoSkill(sock, skillData, nano, targetData);
|
||||||
|
|
||||||
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
||||||
|
@ -80,8 +80,6 @@ struct Player : public Entity, public ICombatant {
|
|||||||
uint64_t iFirstUseFlag[2] = {};
|
uint64_t iFirstUseFlag[2] = {};
|
||||||
time_t lastHeartbeat = 0;
|
time_t lastHeartbeat = 0;
|
||||||
|
|
||||||
int suspicionRating = 0;
|
|
||||||
time_t lastShot = 0;
|
|
||||||
std::vector<sItemBase> buyback = {};
|
std::vector<sItemBase> buyback = {};
|
||||||
|
|
||||||
Player() { kind = EntityKind::PLAYER; }
|
Player() { kind = EntityKind::PLAYER; }
|
||||||
|
@ -23,12 +23,17 @@ using namespace PlayerManager;
|
|||||||
|
|
||||||
std::map<CNSocket*, Player*> PlayerManager::players;
|
std::map<CNSocket*, Player*> PlayerManager::players;
|
||||||
|
|
||||||
static void addPlayer(CNSocket* key, Player *plr) {
|
static void addPlayer(CNSocket* key, Player& plr) {
|
||||||
players[key] = plr;
|
Player *p = new Player();
|
||||||
plr->chunkPos = Chunking::INVALID_CHUNK;
|
|
||||||
plr->lastHeartbeat = 0;
|
|
||||||
|
|
||||||
std::cout << getPlayerName(plr) << " has joined!" << std::endl;
|
// copy object into heap memory
|
||||||
|
*p = plr;
|
||||||
|
|
||||||
|
players[key] = p;
|
||||||
|
p->chunkPos = Chunking::INVALID_CHUNK;
|
||||||
|
p->lastHeartbeat = 0;
|
||||||
|
|
||||||
|
std::cout << getPlayerName(p) << " has joined!" << std::endl;
|
||||||
std::cout << players.size() << " players" << std::endl;
|
std::cout << players.size() << " players" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,72 +215,66 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player *plr = new Player();
|
// for convenience
|
||||||
Database::getPlayer(plr, lm->playerId);
|
Player& plr = lm->plr;
|
||||||
|
|
||||||
// check if account is already in use
|
// check if account is already in use
|
||||||
if (isAccountInUse(plr->accountId)) {
|
if (isAccountInUse(plr.accountId)) {
|
||||||
// kick the other player
|
// kick the other player
|
||||||
exitDuplicate(plr->accountId);
|
exitDuplicate(plr.accountId);
|
||||||
|
|
||||||
// re-read the player from disk, in case it was just flushed
|
|
||||||
*plr = {};
|
|
||||||
Database::getPlayer(plr, lm->playerId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plr->group = nullptr;
|
response.iID = plr.iID;
|
||||||
|
|
||||||
response.iID = plr->iID;
|
|
||||||
response.uiSvrTime = getTime();
|
response.uiSvrTime = getTime();
|
||||||
response.PCLoadData2CL.iUserLevel = plr->accountLevel;
|
response.PCLoadData2CL.iUserLevel = plr.accountLevel;
|
||||||
response.PCLoadData2CL.iHP = plr->HP;
|
response.PCLoadData2CL.iHP = plr.HP;
|
||||||
response.PCLoadData2CL.iLevel = plr->level;
|
response.PCLoadData2CL.iLevel = plr.level;
|
||||||
response.PCLoadData2CL.iCandy = plr->money;
|
response.PCLoadData2CL.iCandy = plr.money;
|
||||||
response.PCLoadData2CL.iFusionMatter = plr->fusionmatter;
|
response.PCLoadData2CL.iFusionMatter = plr.fusionmatter;
|
||||||
response.PCLoadData2CL.iMentor = plr->mentor;
|
response.PCLoadData2CL.iMentor = plr.mentor;
|
||||||
response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had
|
response.PCLoadData2CL.iMentorCount = 1; // how many guides the player has had
|
||||||
response.PCLoadData2CL.iX = plr->x;
|
response.PCLoadData2CL.iX = plr.x;
|
||||||
response.PCLoadData2CL.iY = plr->y;
|
response.PCLoadData2CL.iY = plr.y;
|
||||||
response.PCLoadData2CL.iZ = plr->z;
|
response.PCLoadData2CL.iZ = plr.z;
|
||||||
response.PCLoadData2CL.iAngle = plr->angle;
|
response.PCLoadData2CL.iAngle = plr.angle;
|
||||||
response.PCLoadData2CL.iBatteryN = plr->batteryN;
|
response.PCLoadData2CL.iBatteryN = plr.batteryN;
|
||||||
response.PCLoadData2CL.iBatteryW = plr->batteryW;
|
response.PCLoadData2CL.iBatteryW = plr.batteryW;
|
||||||
response.PCLoadData2CL.iBuddyWarpTime = 60; // sets 60s warp cooldown on login
|
response.PCLoadData2CL.iBuddyWarpTime = 60; // sets 60s warp cooldown on login
|
||||||
|
|
||||||
response.PCLoadData2CL.iWarpLocationFlag = plr->iWarpLocationFlag;
|
response.PCLoadData2CL.iWarpLocationFlag = plr.iWarpLocationFlag;
|
||||||
response.PCLoadData2CL.aWyvernLocationFlag[0] = plr->aSkywayLocationFlag[0];
|
response.PCLoadData2CL.aWyvernLocationFlag[0] = plr.aSkywayLocationFlag[0];
|
||||||
response.PCLoadData2CL.aWyvernLocationFlag[1] = plr->aSkywayLocationFlag[1];
|
response.PCLoadData2CL.aWyvernLocationFlag[1] = plr.aSkywayLocationFlag[1];
|
||||||
|
|
||||||
response.PCLoadData2CL.iActiveNanoSlotNum = -1;
|
response.PCLoadData2CL.iActiveNanoSlotNum = -1;
|
||||||
response.PCLoadData2CL.iFatigue = 50;
|
response.PCLoadData2CL.iFatigue = 50;
|
||||||
response.PCLoadData2CL.PCStyle = plr->PCStyle;
|
response.PCLoadData2CL.PCStyle = plr.PCStyle;
|
||||||
|
|
||||||
// client doesnt read this, it gets it from charinfo
|
// client doesnt read this, it gets it from charinfo
|
||||||
// response.PCLoadData2CL.PCStyle2 = plr->PCStyle2;
|
// response.PCLoadData2CL.PCStyle2 = plr.PCStyle2;
|
||||||
// inventory
|
// inventory
|
||||||
for (int i = 0; i < AEQUIP_COUNT; i++)
|
for (int i = 0; i < AEQUIP_COUNT; i++)
|
||||||
response.PCLoadData2CL.aEquip[i] = plr->Equip[i];
|
response.PCLoadData2CL.aEquip[i] = plr.Equip[i];
|
||||||
for (int i = 0; i < AINVEN_COUNT; i++)
|
for (int i = 0; i < AINVEN_COUNT; i++)
|
||||||
response.PCLoadData2CL.aInven[i] = plr->Inven[i];
|
response.PCLoadData2CL.aInven[i] = plr.Inven[i];
|
||||||
// quest inventory
|
// quest inventory
|
||||||
for (int i = 0; i < AQINVEN_COUNT; i++)
|
for (int i = 0; i < AQINVEN_COUNT; i++)
|
||||||
response.PCLoadData2CL.aQInven[i] = plr->QInven[i];
|
response.PCLoadData2CL.aQInven[i] = plr.QInven[i];
|
||||||
// nanos
|
// nanos
|
||||||
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
for (int i = 1; i < SIZEOF_NANO_BANK_SLOT; i++) {
|
||||||
response.PCLoadData2CL.aNanoBank[i] = plr->Nanos[i];
|
response.PCLoadData2CL.aNanoBank[i] = plr.Nanos[i];
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
response.PCLoadData2CL.aNanoSlots[i] = plr->equippedNanos[i];
|
response.PCLoadData2CL.aNanoSlots[i] = plr.equippedNanos[i];
|
||||||
}
|
}
|
||||||
// missions in progress
|
// missions in progress
|
||||||
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
for (int i = 0; i < ACTIVE_MISSION_COUNT; i++) {
|
||||||
if (plr->tasks[i] == 0)
|
if (plr.tasks[i] == 0)
|
||||||
break;
|
break;
|
||||||
response.PCLoadData2CL.aRunningQuest[i].m_aCurrTaskID = plr->tasks[i];
|
response.PCLoadData2CL.aRunningQuest[i].m_aCurrTaskID = plr.tasks[i];
|
||||||
TaskData &task = *Missions::Tasks[plr->tasks[i]];
|
TaskData &task = *Missions::Tasks[plr.tasks[i]];
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCID[j] = (int)task["m_iCSUEnemyID"][j];
|
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCID[j] = (int)task["m_iCSUEnemyID"][j];
|
||||||
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCCount[j] = plr->RemainingNPCCount[i][j];
|
response.PCLoadData2CL.aRunningQuest[i].m_aKillNPCCount[j] = plr.RemainingNPCCount[i][j];
|
||||||
/*
|
/*
|
||||||
* client doesn't care about NeededItem ID and Count,
|
* client doesn't care about NeededItem ID and Count,
|
||||||
* it gets Count from Quest Inventory
|
* it gets Count from Quest Inventory
|
||||||
@ -285,12 +284,12 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.PCLoadData2CL.iCurrentMissionID = plr->CurrentMissionID;
|
response.PCLoadData2CL.iCurrentMissionID = plr.CurrentMissionID;
|
||||||
|
|
||||||
// completed missions
|
// completed missions
|
||||||
// the packet requires 32 items, but the client only checks the first 16 (shrug)
|
// the packet requires 32 items, but the client only checks the first 16 (shrug)
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
response.PCLoadData2CL.aQuestFlag[i] = plr->aQuestFlag[i];
|
response.PCLoadData2CL.aQuestFlag[i] = plr.aQuestFlag[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computress tips
|
// Computress tips
|
||||||
@ -299,11 +298,11 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX;
|
response.PCLoadData2CL.iFirstUseFlag2 = UINT64_MAX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
response.PCLoadData2CL.iFirstUseFlag1 = plr->iFirstUseFlag[0];
|
response.PCLoadData2CL.iFirstUseFlag1 = plr.iFirstUseFlag[0];
|
||||||
response.PCLoadData2CL.iFirstUseFlag2 = plr->iFirstUseFlag[1];
|
response.PCLoadData2CL.iFirstUseFlag2 = plr.iFirstUseFlag[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
plr->instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter
|
plr.instanceID = INSTANCE_OVERWORLD; // the player should never be in an instance on enter
|
||||||
|
|
||||||
sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1));
|
sock->setEKey(CNSocketEncryption::createNewKey(response.uiSvrTime, response.iID + 1, response.PCLoadData2CL.iFusionMatter + 1));
|
||||||
sock->setFEKey(lm->FEKey);
|
sock->setFEKey(lm->FEKey);
|
||||||
@ -314,14 +313,14 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
// transmit MOTD after entering the game, so the client hopefully changes modes on time
|
// transmit MOTD after entering the game, so the client hopefully changes modes on time
|
||||||
Chat::sendServerMessage(sock, settings::MOTDSTRING);
|
Chat::sendServerMessage(sock, settings::MOTDSTRING);
|
||||||
|
|
||||||
// transfer ownership of Player object into the shard (still valid in this function though)
|
// copy Player object into the shard
|
||||||
addPlayer(sock, plr);
|
addPlayer(sock, plr);
|
||||||
|
|
||||||
// check if there is an expiring vehicle
|
// check if there is an expiring vehicle
|
||||||
Items::checkItemExpire(sock, plr);
|
Items::checkItemExpire(sock, getPlayer(sock));
|
||||||
|
|
||||||
// set player equip stats
|
// set player equip stats
|
||||||
Items::setItemStats(plr);
|
Items::setItemStats(getPlayer(sock));
|
||||||
|
|
||||||
Missions::failInstancedMissions(sock);
|
Missions::failInstancedMissions(sock);
|
||||||
|
|
||||||
@ -332,9 +331,9 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
for (auto& pair : players)
|
for (auto& pair : players)
|
||||||
if (pair.second->notify)
|
if (pair.second->notify)
|
||||||
Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(plr) + " has joined.");
|
Chat::sendServerMessage(pair.first, "[ADMIN]" + getPlayerName(&plr) + " has joined.");
|
||||||
|
|
||||||
// deallocate lm
|
// deallocate lm (and therefore the plr object)
|
||||||
delete lm;
|
delete lm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,11 +400,12 @@ static void revivePlayer(CNSocket* sock, CNPacketData* data) {
|
|||||||
if (!(plr->hasBuff(ECSB_PHOENIX)))
|
if (!(plr->hasBuff(ECSB_PHOENIX)))
|
||||||
return; // sanity check
|
return; // sanity check
|
||||||
plr->Nanos[plr->activeNano].iStamina = 0;
|
plr->Nanos[plr->activeNano].iStamina = 0;
|
||||||
|
// TODO ABILITIES
|
||||||
|
//Abilities::applyBuff(sock, plr->Nanos[plr->activeNano].iSkillID, 2, 1, 0);
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case ePCRegenType::HereByPhoenixGroup: // revived by group member's nano
|
case ePCRegenType::HereByPhoenixGroup: // revived by group member's nano
|
||||||
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: // plain respawn
|
default: // plain respawn
|
||||||
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
plr->HP = PC_MAXHEALTH(plr->level) / 2;
|
||||||
// fallthrough
|
// fallthrough
|
||||||
|
@ -33,22 +33,6 @@ public:
|
|||||||
const char *what() const throw() { return msg.c_str(); }
|
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.
|
* Create a full and properly-paced path by interpolating between keyframes.
|
||||||
*/
|
*/
|
||||||
@ -690,8 +674,6 @@ static void loadEggs(json& eggData, int32_t* nextId) {
|
|||||||
* Load gruntwork output, if it exists
|
* Load gruntwork output, if it exists
|
||||||
*/
|
*/
|
||||||
static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
||||||
if (gruntwork.is_null())
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto paths = gruntwork["paths"];
|
auto paths = gruntwork["paths"];
|
||||||
@ -741,8 +723,8 @@ static void loadGruntworkPre(json& gruntwork, int32_t* nextId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
||||||
if (gruntwork.is_null())
|
|
||||||
return;
|
if (gruntwork.is_null()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// skyway paths
|
// skyway paths
|
||||||
@ -794,8 +776,6 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
|||||||
int id = (*nextId)--;
|
int id = (*nextId)--;
|
||||||
uint64_t instanceID = mob.find("iMapNum") == mob.end() ? INSTANCE_OVERWORLD : (int)mob["iMapNum"];
|
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) {
|
if (NPCManager::NPCData[(int)mob["iNPCType"]]["m_iTeam"] == 2) {
|
||||||
npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"],
|
npc = new Mob(mob["iX"], mob["iY"], mob["iZ"], instanceID, mob["iNPCType"],
|
||||||
NPCManager::NPCData[(int)mob["iNPCType"]], id);
|
NPCManager::NPCData[(int)mob["iNPCType"]], id);
|
||||||
@ -815,9 +795,6 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
|||||||
auto groups = gruntwork["groups"];
|
auto groups = gruntwork["groups"];
|
||||||
for (auto _group = groups.begin(); _group != groups.end(); _group++) {
|
for (auto _group = groups.begin(); _group != groups.end(); _group++) {
|
||||||
auto leader = _group.value();
|
auto leader = _group.value();
|
||||||
|
|
||||||
ensureValidNPCType((int)leader["iNPCType"], settings::GRUNTWORKJSON);
|
|
||||||
|
|
||||||
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
||||||
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
||||||
|
|
||||||
@ -838,9 +815,6 @@ static void loadGruntworkPost(json& gruntwork, int32_t* nextId) {
|
|||||||
int followerCount = 0;
|
int followerCount = 0;
|
||||||
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
||||||
auto follower = _fol.value();
|
auto follower = _fol.value();
|
||||||
|
|
||||||
ensureValidNPCType((int)follower["iNPCType"], settings::GRUNTWORKJSON);
|
|
||||||
|
|
||||||
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
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);
|
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);
|
||||||
|
|
||||||
@ -897,9 +871,10 @@ static void loadNPCs(json& npcData) {
|
|||||||
npcID += NPC_ID_OFFSET;
|
npcID += NPC_ID_OFFSET;
|
||||||
int instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
int instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
||||||
int type = (int)npc["iNPCType"];
|
int type = (int)npc["iNPCType"];
|
||||||
|
if (NPCManager::NPCData[type].is_null()) {
|
||||||
ensureValidNPCType(type, settings::NPCJSON);
|
std::cout << "[WARN] NPC type " << type << " not found; skipping (json#" << _npc.key() << ")" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
#ifdef ACADEMY
|
#ifdef ACADEMY
|
||||||
// do not spawn NPCs in the future
|
// do not spawn NPCs in the future
|
||||||
if (npc["iX"] > 512000 && npc["iY"] < 256000)
|
if (npc["iX"] > 512000 && npc["iY"] < 256000)
|
||||||
@ -941,9 +916,10 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
|||||||
int npcID = std::strtol(_npc.key().c_str(), nullptr, 10); // parse ID string to integer
|
int npcID = std::strtol(_npc.key().c_str(), nullptr, 10); // parse ID string to integer
|
||||||
npcID += MOB_ID_OFFSET;
|
npcID += MOB_ID_OFFSET;
|
||||||
int type = (int)npc["iNPCType"];
|
int type = (int)npc["iNPCType"];
|
||||||
|
if (NPCManager::NPCData[type].is_null()) {
|
||||||
ensureValidNPCType(type, settings::MOBJSON);
|
std::cout << "[WARN] NPC type " << type << " not found; skipping (json#" << _npc.key() << ")" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto td = NPCManager::NPCData[type];
|
auto td = NPCManager::NPCData[type];
|
||||||
uint64_t instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
uint64_t instanceID = npc.find("iMapNum") == npc.end() ? INSTANCE_OVERWORLD : (int)npc["iMapNum"];
|
||||||
|
|
||||||
@ -971,10 +947,7 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
|||||||
for (json::iterator _group = groupData.begin(); _group != groupData.end(); _group++) {
|
for (json::iterator _group = groupData.begin(); _group != groupData.end(); _group++) {
|
||||||
auto leader = _group.value();
|
auto leader = _group.value();
|
||||||
int leadID = std::strtol(_group.key().c_str(), nullptr, 10); // parse ID string to integer
|
int leadID = std::strtol(_group.key().c_str(), nullptr, 10); // parse ID string to integer
|
||||||
|
|
||||||
leadID += MOB_GROUP_ID_OFFSET;
|
leadID += MOB_GROUP_ID_OFFSET;
|
||||||
ensureValidNPCType(leader["iNPCType"], settings::MOBJSON);
|
|
||||||
|
|
||||||
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
auto td = NPCManager::NPCData[(int)leader["iNPCType"]];
|
||||||
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
uint64_t instanceID = leader.find("iMapNum") == leader.end() ? INSTANCE_OVERWORLD : (int)leader["iMapNum"];
|
||||||
auto followers = leader["aFollowers"];
|
auto followers = leader["aFollowers"];
|
||||||
@ -1004,9 +977,6 @@ static void loadMobs(json& npcData, int32_t* nextId) {
|
|||||||
int followerCount = 0;
|
int followerCount = 0;
|
||||||
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
for (json::iterator _fol = followers.begin(); _fol != followers.end(); _fol++) {
|
||||||
auto follower = _fol.value();
|
auto follower = _fol.value();
|
||||||
|
|
||||||
ensureValidNPCType(follower["iNPCType"], settings::MOBJSON);
|
|
||||||
|
|
||||||
auto tdFol = NPCManager::NPCData[(int)follower["iNPCType"]];
|
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);
|
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);
|
||||||
|
|
||||||
@ -1112,39 +1082,19 @@ void TableData::init() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// load JSON data into tables
|
// load JSON data into tables
|
||||||
|
std::ifstream fstream;
|
||||||
for (int i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
std::pair<json*, std::string>& table = tables[i];
|
std::pair<json*, std::string>& table = tables[i];
|
||||||
|
|
||||||
// scope for fstream
|
|
||||||
{
|
|
||||||
std::ifstream fstream;
|
|
||||||
fstream.open(settings::TDATADIR + "/" + table.second); // open file
|
fstream.open(settings::TDATADIR + "/" + table.second); // open file
|
||||||
|
if (!fstream.fail()) {
|
||||||
// did we fail to open the file?
|
fstream >> *table.first; // load file contents into table
|
||||||
if (fstream.fail()) {
|
} else {
|
||||||
// gruntwork isn't critical
|
if (table.first != &gruntwork) { // gruntwork isn't critical
|
||||||
if (table.first == &gruntwork)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::cerr << "[FATAL] Critical tdata file missing: " << table.second << std::endl;
|
std::cerr << "[FATAL] Critical tdata file missing: " << table.second << std::endl;
|
||||||
exit(1);
|
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
|
// patching: load each patch directory specified in the config file
|
||||||
|
|
||||||
@ -1159,11 +1109,11 @@ void TableData::init() {
|
|||||||
std::string patchModuleName = *it;
|
std::string patchModuleName = *it;
|
||||||
std::string patchFile = settings::PATCHDIR + patchModuleName + "/" + table.second;
|
std::string patchFile = settings::PATCHDIR + patchModuleName + "/" + table.second;
|
||||||
try {
|
try {
|
||||||
std::ifstream fstream;
|
|
||||||
fstream.open(patchFile);
|
fstream.open(patchFile);
|
||||||
fstream >> patch; // load into temporary json object
|
fstream >> patch; // load into temporary json object
|
||||||
std::cout << "[INFO] Patching " << patchFile << std::endl;
|
std::cout << "[INFO] Patching " << patchFile << std::endl;
|
||||||
patchJSON(table.first, &patch); // patch
|
patchJSON(table.first, &patch); // patch
|
||||||
|
fstream.close();
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "servers/CNShardServer.hpp"
|
#include "servers/CNShardServer.hpp"
|
||||||
|
|
||||||
#include "PlayerManager.hpp"
|
#include "PlayerManager.hpp"
|
||||||
#include "db/Database.hpp"
|
|
||||||
|
|
||||||
using namespace Trading;
|
using namespace Trading;
|
||||||
|
|
||||||
@ -273,8 +272,6 @@ static void tradeConfirm(CNSocket* sock, CNPacketData* data) {
|
|||||||
otherSock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
|
otherSock->sendPacket(msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::commitTrade(plr, plr2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) {
|
static void tradeConfirmCancel(CNSocket* sock, CNPacketData* data) {
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
#include "Items.hpp"
|
#include "Items.hpp"
|
||||||
#include "Rand.hpp"
|
#include "Rand.hpp"
|
||||||
|
|
||||||
// 7 days
|
|
||||||
#define VEHICLE_EXPIRY_DURATION 604800
|
|
||||||
|
|
||||||
using namespace Vendors;
|
using namespace Vendors;
|
||||||
|
|
||||||
std::map<int32_t, std::vector<VendorListing>> Vendors::VendorTables;
|
std::map<int32_t, std::vector<VendorListing>> Vendors::VendorTables;
|
||||||
@ -61,8 +58,8 @@ static void vendorBuy(CNSocket* sock, CNPacketData* data) {
|
|||||||
|
|
||||||
// if vehicle
|
// if vehicle
|
||||||
if (req->Item.iType == 10) {
|
if (req->Item.iType == 10) {
|
||||||
// set time limit: current time + expiry duration
|
// set time limit: current time + 7days
|
||||||
req->Item.iTimeLimit = getTimestamp() + VEHICLE_EXPIRY_DURATION;
|
req->Item.iTimeLimit = getTimestamp() + 604800;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slot != req->iInvenSlotNum) {
|
if (slot != req->iInvenSlotNum) {
|
||||||
@ -232,20 +229,12 @@ static void vendorTable(CNSocket* sock, CNPacketData* data) {
|
|||||||
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC, resp);
|
INITSTRUCT(sP_FE2CL_REP_PC_VENDOR_TABLE_UPDATE_SUCC, resp);
|
||||||
|
|
||||||
for (int i = 0; i < (int)listings.size() && i < 20; i++) { // 20 is the max
|
for (int i = 0; i < (int)listings.size() && i < 20; i++) { // 20 is the max
|
||||||
sItemBase base = {};
|
sItemBase base;
|
||||||
base.iID = listings[i].id;
|
base.iID = listings[i].id;
|
||||||
|
base.iOpt = 0;
|
||||||
|
base.iTimeLimit = 0;
|
||||||
base.iType = listings[i].type;
|
base.iType = listings[i].type;
|
||||||
|
|
||||||
/*
|
|
||||||
* Set vehicle expiry value.
|
|
||||||
*
|
|
||||||
* Note: sItemBase.iTimeLimit in the context of vendor listings contains
|
|
||||||
* a duration, unlike in most other contexts where it contains the
|
|
||||||
* expiration timestamp.
|
|
||||||
*/
|
|
||||||
if (listings[i].type == 10)
|
|
||||||
base.iTimeLimit = VEHICLE_EXPIRY_DURATION;
|
|
||||||
|
|
||||||
sItemVendor vItem;
|
sItemVendor vItem;
|
||||||
vItem.item = base;
|
vItem.item = base;
|
||||||
vItem.iSortNum = listings[i].sort;
|
vItem.iSortNum = listings[i].sort;
|
||||||
|
@ -41,8 +41,7 @@ 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 CNSocketEncryption::createNewKey(uint64_t uTime, int32_t iv1, int32_t iv2) {
|
||||||
uint64_t num = (uint64_t)(iv1 + 1);
|
uint64_t num = (uint64_t)(iv1 + 1);
|
||||||
uint64_t num2 = (uint64_t)(iv2 + 1);
|
uint64_t num2 = (uint64_t)(iv2 + 1);
|
||||||
uint64_t dEKey;
|
uint64_t dEKey = (uint64_t)(*(uint64_t*)&defaultKey[0]);
|
||||||
memcpy(&dEKey, defaultKey, sizeof(dEKey));
|
|
||||||
return dEKey * (uTime * num * num2);
|
return dEKey * (uTime * num * num2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +65,7 @@ CNPacketData::CNPacketData(void *b, uint32_t t, int l, int trnum, void *trs):
|
|||||||
// ========================================================[[ CNSocket ]]========================================================
|
// ========================================================[[ CNSocket ]]========================================================
|
||||||
|
|
||||||
CNSocket::CNSocket(SOCKET s, struct sockaddr_in &addr, PacketHandler ph): sock(s), sockaddr(addr), pHandler(ph) {
|
CNSocket::CNSocket(SOCKET s, struct sockaddr_in &addr, PacketHandler ph): sock(s), sockaddr(addr), pHandler(ph) {
|
||||||
memcpy(&EKey, CNSocketEncryption::defaultKey, sizeof(EKey));
|
EKey = (uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CNSocket::sendData(uint8_t* data, int size) {
|
bool CNSocket::sendData(uint8_t* data, int size) {
|
||||||
@ -110,11 +109,7 @@ bool CNSocket::isAlive() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CNSocket::kill() {
|
void CNSocket::kill() {
|
||||||
if (!alive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
alive = false;
|
alive = false;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
shutdown(sock, SD_BOTH);
|
shutdown(sock, SD_BOTH);
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
@ -246,10 +241,9 @@ void CNSocket::step() {
|
|||||||
if (readSize <= 0) {
|
if (readSize <= 0) {
|
||||||
// we aren't reading a packet yet, try to start looking for one
|
// we aren't reading a packet yet, try to start looking for one
|
||||||
int recved = recv(sock, (buffer_t*)readBuffer, sizeof(int32_t), 0);
|
int recved = recv(sock, (buffer_t*)readBuffer, sizeof(int32_t), 0);
|
||||||
if (recved >= 0 && recved < sizeof(int32_t)) {
|
if (recved == 0) {
|
||||||
// too little data for readSize or the socket was closed normally (when 0 bytes were read)
|
// the socket was closed normally
|
||||||
kill();
|
kill();
|
||||||
return;
|
|
||||||
} else if (!SOCKETERROR(recved)) {
|
} else if (!SOCKETERROR(recved)) {
|
||||||
// we got our packet size!!!!
|
// we got our packet size!!!!
|
||||||
readSize = *((int32_t*)readBuffer);
|
readSize = *((int32_t*)readBuffer);
|
||||||
@ -270,12 +264,11 @@ void CNSocket::step() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (readSize > 0 && readBufferIndex < readSize) {
|
if (readSize > 0 && readBufferIndex < readSize) {
|
||||||
// read until the end of the packet (or at least try to)
|
// read until the end of the packet! (or at least try too)
|
||||||
int recved = recv(sock, (buffer_t*)(readBuffer + readBufferIndex), readSize - readBufferIndex, 0);
|
int recved = recv(sock, (buffer_t*)(readBuffer + readBufferIndex), readSize - readBufferIndex, 0);
|
||||||
if (recved == 0) {
|
if (recved == 0) {
|
||||||
// the socket was closed normally
|
// the socket was closed normally
|
||||||
kill();
|
kill();
|
||||||
return;
|
|
||||||
} else if (!SOCKETERROR(recved))
|
} else if (!SOCKETERROR(recved))
|
||||||
readBufferIndex += recved;
|
readBufferIndex += recved;
|
||||||
else if (OF_ERRNO != OF_EWOULD) {
|
else if (OF_ERRNO != OF_EWOULD) {
|
||||||
@ -418,9 +411,9 @@ void CNServer::addPollFD(SOCKET s) {
|
|||||||
fds.push_back({s, POLLIN});
|
fds.push_back({s, POLLIN});
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNServer::removePollFD(int fd) {
|
void CNServer::removePollFD(int i) {
|
||||||
auto it = fds.begin();
|
auto it = fds.begin();
|
||||||
while (it != fds.end() && it->fd != fd)
|
while (it != fds.end() && it->fd != fds[i].fd)
|
||||||
it++;
|
it++;
|
||||||
assert(it != fds.end());
|
assert(it != fds.end());
|
||||||
|
|
||||||
@ -465,7 +458,7 @@ void CNServer::start() {
|
|||||||
if (!setSockNonblocking(sock, newConnectionSocket))
|
if (!setSockNonblocking(sock, newConnectionSocket))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::cout << "New " << serverType << " connection! " << inet_ntoa(address.sin_addr) << std::endl;
|
std::cout << "New connection! " << inet_ntoa(address.sin_addr) << std::endl;
|
||||||
|
|
||||||
addPollFD(newConnectionSocket);
|
addPollFD(newConnectionSocket);
|
||||||
|
|
||||||
@ -480,15 +473,10 @@ void CNServer::start() {
|
|||||||
} else {
|
} else {
|
||||||
std::lock_guard<std::mutex> lock(activeCrit); // protect operations on connections
|
std::lock_guard<std::mutex> lock(activeCrit); // protect operations on connections
|
||||||
|
|
||||||
// halt packet handling if server is shutting down
|
|
||||||
if (!active)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// player sockets
|
// player sockets
|
||||||
if (connections.find(fds[i].fd) == connections.end()) {
|
if (connections.find(fds[i].fd) == connections.end()) {
|
||||||
std::cout << "[FATAL] Event on non-existent socket: " << fds[i].fd << std::endl;
|
std::cout << "[WARN] Event on non-existant socket?" << std::endl;
|
||||||
assert(0);
|
continue; // just to be safe
|
||||||
/* not reached */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CNSocket* cSock = connections[fds[i].fd];
|
CNSocket* cSock = connections[fds[i].fd];
|
||||||
@ -497,29 +485,22 @@ void CNServer::start() {
|
|||||||
if (fds[i].revents & ~POLLIN)
|
if (fds[i].revents & ~POLLIN)
|
||||||
cSock->kill();
|
cSock->kill();
|
||||||
|
|
||||||
if (cSock->isAlive())
|
if (cSock->isAlive()) {
|
||||||
cSock->step();
|
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();
|
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +230,6 @@ protected:
|
|||||||
const size_t STARTFDSCOUNT = 8; // number of initial PollFD slots
|
const size_t STARTFDSCOUNT = 8; // number of initial PollFD slots
|
||||||
std::vector<PollFD> fds;
|
std::vector<PollFD> fds;
|
||||||
|
|
||||||
std::string serverType = "invalid";
|
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
socklen_t addressSize;
|
socklen_t addressSize;
|
||||||
|
@ -32,7 +32,7 @@ void CNShared::pruneLoginMetadata(CNServer *serv, time_t currTime) {
|
|||||||
auto& sk = it->first;
|
auto& sk = it->first;
|
||||||
auto& lm = it->second;
|
auto& lm = it->second;
|
||||||
|
|
||||||
if (currTime > lm->timestamp + CNSHARED_TIMEOUT) {
|
if (lm->timestamp + CNSHARED_TIMEOUT > currTime) {
|
||||||
std::cout << "[WARN] Pruning hung connection attempt" << std::endl;
|
std::cout << "[WARN] Pruning hung connection attempt" << std::endl;
|
||||||
|
|
||||||
// deallocate object and remove map entry
|
// deallocate object and remove map entry
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Connecions time out after 5 minutes, checked every 30 seconds.
|
* Connecions time out after 15 minutes, checked every 30 seconds.
|
||||||
*/
|
*/
|
||||||
#define CNSHARED_TIMEOUT 300000
|
#define CNSHARED_TIMEOUT 900000
|
||||||
#define CNSHARED_PERIOD 30000
|
#define CNSHARED_PERIOD 30000
|
||||||
|
|
||||||
struct LoginMetadata {
|
struct LoginMetadata {
|
||||||
uint64_t FEKey;
|
uint64_t FEKey;
|
||||||
int32_t playerId;
|
Player plr;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ namespace Database {
|
|||||||
uint64_t Timestamp;
|
uint64_t Timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
void init();
|
|
||||||
void open();
|
void open();
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
@ -53,8 +52,7 @@ namespace Database {
|
|||||||
bool banPlayer(int playerId, std::string& reason);
|
bool banPlayer(int playerId, std::string& reason);
|
||||||
bool unbanPlayer(int playerId);
|
bool unbanPlayer(int playerId);
|
||||||
|
|
||||||
void updateSelected(int accountId, int slot);
|
void updateSelected(int accountId, int playerId);
|
||||||
void updateSelectedByPlayerId(int accountId, int playerId);
|
|
||||||
|
|
||||||
bool validateCharacter(int characterID, int userID);
|
bool validateCharacter(int characterID, int userID);
|
||||||
bool isNameFree(std::string firstName, std::string lastName);
|
bool isNameFree(std::string firstName, std::string lastName);
|
||||||
@ -80,9 +78,7 @@ namespace Database {
|
|||||||
|
|
||||||
// getting players
|
// getting players
|
||||||
void getPlayer(Player* plr, int id);
|
void getPlayer(Player* plr, int id);
|
||||||
bool _updatePlayer(Player *player);
|
|
||||||
void updatePlayer(Player *player);
|
void updatePlayer(Player *player);
|
||||||
void commitTrade(Player *plr1, Player *plr2);
|
|
||||||
|
|
||||||
// buddies
|
// buddies
|
||||||
int getNumBuddies(Player* player);
|
int getNumBuddies(Player* player);
|
||||||
@ -102,7 +98,7 @@ namespace Database {
|
|||||||
void deleteEmailAttachments(int playerID, int index, int slot);
|
void deleteEmailAttachments(int playerID, int index, int slot);
|
||||||
void deleteEmails(int playerID, int64_t* indices);
|
void deleteEmails(int playerID, int64_t* indices);
|
||||||
int getNextEmailIndex(int playerID);
|
int getNextEmailIndex(int playerID);
|
||||||
bool sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender);
|
bool sendEmail(EmailData* data, std::vector<sItemBase> attachments);
|
||||||
|
|
||||||
// racing
|
// racing
|
||||||
RaceRanking getTopRaceRanking(int epID, int playerID);
|
RaceRanking getTopRaceRanking(int epID, int playerID);
|
||||||
|
@ -152,8 +152,7 @@ void Database::updateEmailContent(EmailData* data) {
|
|||||||
sqlite3_step(stmt);
|
sqlite3_step(stmt);
|
||||||
int attachmentsCount = sqlite3_column_int(stmt, 0);
|
int attachmentsCount = sqlite3_column_int(stmt, 0);
|
||||||
|
|
||||||
// set attachment flag dynamically
|
data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0; // set attachment flag dynamically
|
||||||
data->ItemFlag = (data->Taros > 0 || attachmentsCount > 0) ? 1 : 0;
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
@ -266,7 +265,7 @@ int Database::getNextEmailIndex(int playerID) {
|
|||||||
return (index > 0 ? index + 1 : 1);
|
return (index > 0 ? index + 1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments, Player *sender) {
|
bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||||
@ -331,13 +330,6 @@ bool Database::sendEmail(EmailData* data, std::vector<sItemBase> attachments, Pl
|
|||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
if (!_updatePlayer(sender)) {
|
|
||||||
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
|
||||||
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
|
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -236,20 +236,7 @@ static int getTableSize(std::string tableName) {
|
|||||||
return result;
|
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() {
|
void Database::open() {
|
||||||
|
|
||||||
// XXX: move locks here
|
// XXX: move locks here
|
||||||
int rc = sqlite3_open(settings::DBPATH.c_str(), &db);
|
int rc = sqlite3_open(settings::DBPATH.c_str(), &db);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
|
@ -3,15 +3,6 @@
|
|||||||
#include "db/Database.hpp"
|
#include "db/Database.hpp"
|
||||||
#include <sqlite3.h>
|
#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 std::mutex dbCrit;
|
||||||
extern sqlite3 *db;
|
extern sqlite3 *db;
|
||||||
|
|
||||||
|
@ -79,29 +79,6 @@ void Database::updateSelected(int accountId, int slot) {
|
|||||||
std::cout << "[WARN] Database fail on updateSelected(): " << sqlite3_errmsg(db) << std::endl;
|
std::cout << "[WARN] Database fail on updateSelected(): " << sqlite3_errmsg(db) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::updateSelectedByPlayerId(int accountId, int32_t playerId) {
|
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
|
||||||
|
|
||||||
const char* sql = R"(
|
|
||||||
UPDATE Accounts SET
|
|
||||||
Selected = p.Slot,
|
|
||||||
LastLogin = (strftime('%s', 'now'))
|
|
||||||
FROM (SELECT Slot From Players WHERE PlayerId = ?) AS p
|
|
||||||
WHERE AccountID = ?;
|
|
||||||
)";
|
|
||||||
|
|
||||||
sqlite3_stmt* stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
|
||||||
sqlite3_bind_int(stmt, 1, playerId);
|
|
||||||
sqlite3_bind_int(stmt, 2, accountId);
|
|
||||||
int rc = sqlite3_step(stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
if (rc != SQLITE_DONE)
|
|
||||||
std::cout << "[WARN] Database fail on updateSelectedByPlayerId(): " << sqlite3_errmsg(db) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Database::validateCharacter(int characterID, int userID) {
|
bool Database::validateCharacter(int characterID, int userID) {
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
|
|
||||||
|
@ -285,13 +285,11 @@ void Database::getPlayer(Player* plr, int id) {
|
|||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void Database::updatePlayer(Player *player) {
|
||||||
* Low-level function to save a player to DB.
|
std::lock_guard<std::mutex> lock(dbCrit);
|
||||||
* Must be run in a SQL transaction and with dbCrit locked.
|
|
||||||
* The caller manages the transacstion, so if this function returns false,
|
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||||
* the caller must roll it back.
|
|
||||||
*/
|
|
||||||
bool Database::_updatePlayer(Player *player) {
|
|
||||||
const char* sql = R"(
|
const char* sql = R"(
|
||||||
UPDATE Players
|
UPDATE Players
|
||||||
SET
|
SET
|
||||||
@ -338,8 +336,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 21, player->iID);
|
sqlite3_bind_int(stmt, 21, player->iID);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
@ -375,8 +375,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -393,8 +395,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit);
|
sqlite3_bind_int(stmt, 6, player->Inven[i].iTimeLimit);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -411,8 +415,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit);
|
sqlite3_bind_int(stmt, 6, player->Bank[i].iTimeLimit);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -445,8 +451,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 4, player->QInven[i].iID);
|
sqlite3_bind_int(stmt, 4, player->QInven[i].iID);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -479,8 +487,10 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina);
|
sqlite3_bind_int(stmt, 4, player->Nanos[i].iStamina);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
@ -514,47 +524,14 @@ bool Database::_updatePlayer(Player *player) {
|
|||||||
sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]);
|
sqlite3_bind_int(stmt, 5, player->RemainingNPCCount[i][2]);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||||
|
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
||||||
|
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
sqlite3_reset(stmt);
|
sqlite3_reset(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Database::updatePlayer(Player *player) {
|
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
|
||||||
|
|
||||||
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (!_updatePlayer(player)) {
|
|
||||||
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
|
||||||
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Database::commitTrade(Player *plr1, Player *plr2) {
|
|
||||||
std::lock_guard<std::mutex> lock(dbCrit);
|
|
||||||
|
|
||||||
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (!_updatePlayer(plr1)) {
|
|
||||||
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
|
||||||
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_updatePlayer(plr2)) {
|
|
||||||
std::cout << "[WARN] Database: Failed to save player to database: " << sqlite3_errmsg(db) << std::endl;
|
|
||||||
sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
14
src/main.cpp
14
src/main.cpp
@ -98,9 +98,6 @@ void initsignals() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "[INFO] OpenFusion v" GIT_VERSION << std::endl;
|
|
||||||
std::cout << "[INFO] Protocol version: " << PROTOCOL_VERSION << std::endl;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
|
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
|
||||||
@ -108,15 +105,15 @@ int main() {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
initsignals();
|
initsignals();
|
||||||
settings::init();
|
settings::init();
|
||||||
Database::init();
|
|
||||||
Rand::init(getTime());
|
|
||||||
TableData::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;
|
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
|
||||||
|
|
||||||
|
Rand::init(getTime());
|
||||||
|
TableData::init();
|
||||||
PlayerManager::init();
|
PlayerManager::init();
|
||||||
PlayerMovement::init();
|
PlayerMovement::init();
|
||||||
BuiltinCommands::init();
|
BuiltinCommands::init();
|
||||||
@ -135,9 +132,8 @@ int main() {
|
|||||||
Email::init();
|
Email::init();
|
||||||
Groups::init();
|
Groups::init();
|
||||||
Racing::init();
|
Racing::init();
|
||||||
Trading::init();
|
|
||||||
|
|
||||||
Database::open();
|
Database::open();
|
||||||
|
Trading::init();
|
||||||
|
|
||||||
switch (settings::EVENTMODE) {
|
switch (settings::EVENTMODE) {
|
||||||
case 0: break; // no event
|
case 0: break; // no event
|
||||||
|
@ -153,18 +153,15 @@ static sock_filter filter[] = {
|
|||||||
ALLOW_SYSCALL(read),
|
ALLOW_SYSCALL(read),
|
||||||
ALLOW_SYSCALL(write),
|
ALLOW_SYSCALL(write),
|
||||||
ALLOW_SYSCALL(close),
|
ALLOW_SYSCALL(close),
|
||||||
#ifdef __NR_stat
|
#if __NR_stat
|
||||||
ALLOW_SYSCALL(stat),
|
ALLOW_SYSCALL(stat),
|
||||||
#endif
|
#endif
|
||||||
ALLOW_SYSCALL(fstat),
|
ALLOW_SYSCALL(fstat),
|
||||||
#ifdef __NR_newfstatat
|
|
||||||
ALLOW_SYSCALL(newfstatat),
|
|
||||||
#endif
|
|
||||||
ALLOW_SYSCALL(fsync), // maybe
|
ALLOW_SYSCALL(fsync), // maybe
|
||||||
#ifdef __NR_creat
|
#if __NR_creat
|
||||||
ALLOW_SYSCALL(creat), // maybe; for DB journal
|
ALLOW_SYSCALL(creat), // maybe; for DB journal
|
||||||
#endif
|
#endif
|
||||||
#ifdef __NR_unlink
|
#if __NR_unlink
|
||||||
ALLOW_SYSCALL(unlink), // for DB journal
|
ALLOW_SYSCALL(unlink), // for DB journal
|
||||||
#endif
|
#endif
|
||||||
ALLOW_SYSCALL(lseek), // musl-libc; alt DB
|
ALLOW_SYSCALL(lseek), // musl-libc; alt DB
|
||||||
@ -195,9 +192,6 @@ static sock_filter filter[] = {
|
|||||||
ALLOW_SYSCALL(exit_group),
|
ALLOW_SYSCALL(exit_group),
|
||||||
ALLOW_SYSCALL(rt_sigprocmask), // musl-libc
|
ALLOW_SYSCALL(rt_sigprocmask), // musl-libc
|
||||||
ALLOW_SYSCALL(clock_nanosleep), // gets called very rarely
|
ALLOW_SYSCALL(clock_nanosleep), // gets called very rarely
|
||||||
#ifdef __NR_rseq
|
|
||||||
ALLOW_SYSCALL(rseq),
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// to crash properly on SIGSEGV
|
// to crash properly on SIGSEGV
|
||||||
DENY_SYSCALL_ERRNO(tgkill, EPERM),
|
DENY_SYSCALL_ERRNO(tgkill, EPERM),
|
||||||
@ -280,7 +274,7 @@ static sock_filter filter[] = {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// AArch64 (ARM64)
|
// AArch64 (ARM64)
|
||||||
#ifdef __NR_unlinkat
|
#if __NR_unlinkat
|
||||||
ALLOW_SYSCALL(unlinkat),
|
ALLOW_SYSCALL(unlinkat),
|
||||||
#endif
|
#endif
|
||||||
#ifdef __NR_fstatat64
|
#ifdef __NR_fstatat64
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
|
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
|
||||||
|
|
||||||
CNLoginServer::CNLoginServer(uint16_t p) {
|
CNLoginServer::CNLoginServer(uint16_t p) {
|
||||||
serverType = "login";
|
|
||||||
port = p;
|
port = p;
|
||||||
pHandler = &CNLoginServer::handlePacket;
|
pHandler = &CNLoginServer::handlePacket;
|
||||||
init();
|
init();
|
||||||
@ -211,12 +210,9 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
|
|||||||
// send the resp in with original key
|
// send the resp in with original key
|
||||||
sock->sendPacket(resp, P_LS2CL_REP_LOGIN_SUCC);
|
sock->sendPacket(resp, P_LS2CL_REP_LOGIN_SUCC);
|
||||||
|
|
||||||
uint64_t defaultKey;
|
|
||||||
memcpy(&defaultKey, CNSocketEncryption::defaultKey, sizeof(defaultKey));
|
|
||||||
|
|
||||||
// update keys
|
// update keys
|
||||||
sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1));
|
sock->setEKey(CNSocketEncryption::createNewKey(resp.uiSvrTime, resp.iCharCount + 1, resp.iSlotNum + 1));
|
||||||
sock->setFEKey(CNSocketEncryption::createNewKey(defaultKey, login->iClientVerC, 1));
|
sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1));
|
||||||
|
|
||||||
DEBUGLOG(
|
DEBUGLOG(
|
||||||
std::cout << "Login Server: Login success. Welcome " << userLogin << " [" << loginSessions[sock].userID << "]" << std::endl;
|
std::cout << "Login Server: Login success. Welcome " << userLogin << " [" << loginSessions[sock].userID << "]" << std::endl;
|
||||||
@ -477,7 +473,11 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) {
|
|||||||
LoginMetadata *lm = new LoginMetadata();
|
LoginMetadata *lm = new LoginMetadata();
|
||||||
lm->FEKey = sock->getFEKey();
|
lm->FEKey = sock->getFEKey();
|
||||||
lm->timestamp = getTime();
|
lm->timestamp = getTime();
|
||||||
lm->playerId = selection->iPC_UID;
|
|
||||||
|
Database::getPlayer(&lm->plr, selection->iPC_UID);
|
||||||
|
// this should never happen but for extra safety
|
||||||
|
if (lm->plr.iID == 0)
|
||||||
|
return invalidCharacter(sock);
|
||||||
|
|
||||||
resp.iEnterSerialKey = Rand::cryptoRand();
|
resp.iEnterSerialKey = Rand::cryptoRand();
|
||||||
|
|
||||||
@ -487,7 +487,7 @@ void CNLoginServer::characterSelect(CNSocket* sock, CNPacketData* data) {
|
|||||||
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
|
sock->sendPacket(resp, P_LS2CL_REP_SHARD_SELECT_SUCC);
|
||||||
|
|
||||||
// update current slot in DB
|
// update current slot in DB
|
||||||
Database::updateSelectedByPlayerId(loginSessions[sock].userID, selection->iPC_UID);
|
Database::updateSelected(loginSessions[sock].userID, lm->plr.slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
void CNLoginServer::finishTutorial(CNSocket* sock, CNPacketData* data) {
|
||||||
|
@ -18,7 +18,6 @@ std::map<uint32_t, PacketHandler> CNShardServer::ShardPackets;
|
|||||||
std::list<TimerEvent> CNShardServer::Timers;
|
std::list<TimerEvent> CNShardServer::Timers;
|
||||||
|
|
||||||
CNShardServer::CNShardServer(uint16_t p) {
|
CNShardServer::CNShardServer(uint16_t p) {
|
||||||
serverType = "shard";
|
|
||||||
port = p;
|
port = p;
|
||||||
pHandler = &CNShardServer::handlePacket;
|
pHandler = &CNShardServer::handlePacket;
|
||||||
REGISTER_SHARD_TIMER(keepAliveTimer, 4000);
|
REGISTER_SHARD_TIMER(keepAliveTimer, 4000);
|
||||||
@ -118,10 +117,6 @@ void CNShardServer::kill() {
|
|||||||
void CNShardServer::onStep() {
|
void CNShardServer::onStep() {
|
||||||
time_t currTime = getTime();
|
time_t currTime = getTime();
|
||||||
|
|
||||||
// do not evaluate timers if the server is shutting down
|
|
||||||
if (!active)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (TimerEvent& event : Timers) {
|
for (TimerEvent& event : Timers) {
|
||||||
if (event.scheduledEvent == 0) {
|
if (event.scheduledEvent == 0) {
|
||||||
// event hasn't been queued yet, go ahead and do that
|
// event hasn't been queued yet, go ahead and do that
|
||||||
|
@ -21,7 +21,6 @@ bool settings::LOCALHOSTWORKAROUND = true;
|
|||||||
time_t settings::TIMEOUT = 60000;
|
time_t settings::TIMEOUT = 60000;
|
||||||
int settings::VIEWDISTANCE = 25600;
|
int settings::VIEWDISTANCE = 25600;
|
||||||
bool settings::SIMULATEMOBS = true;
|
bool settings::SIMULATEMOBS = true;
|
||||||
bool settings::ANTICHEAT = true;
|
|
||||||
|
|
||||||
// default spawn point
|
// default spawn point
|
||||||
#ifndef ACADEMY
|
#ifndef ACADEMY
|
||||||
@ -110,7 +109,6 @@ void settings::init() {
|
|||||||
ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL);
|
ACCLEVEL = reader.GetInteger("shard", "accountlevel", ACCLEVEL);
|
||||||
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
|
EVENTMODE = reader.GetInteger("shard", "eventmode", EVENTMODE);
|
||||||
DISABLEFIRSTUSEFLAG = reader.GetBoolean("shard", "disablefirstuseflag", DISABLEFIRSTUSEFLAG);
|
DISABLEFIRSTUSEFLAG = reader.GetBoolean("shard", "disablefirstuseflag", DISABLEFIRSTUSEFLAG);
|
||||||
ANTICHEAT = reader.GetBoolean("shard", "anticheat", ANTICHEAT);
|
|
||||||
MONITORENABLED = reader.GetBoolean("monitor", "enabled", MONITORENABLED);
|
MONITORENABLED = reader.GetBoolean("monitor", "enabled", MONITORENABLED);
|
||||||
MONITORPORT = reader.GetInteger("monitor", "port", MONITORPORT);
|
MONITORPORT = reader.GetInteger("monitor", "port", MONITORPORT);
|
||||||
MONITORINTERVAL = reader.GetInteger("monitor", "interval", MONITORINTERVAL);
|
MONITORINTERVAL = reader.GetInteger("monitor", "interval", MONITORINTERVAL);
|
||||||
|
@ -12,7 +12,6 @@ namespace settings {
|
|||||||
extern int SHARDPORT;
|
extern int SHARDPORT;
|
||||||
extern std::string SHARDSERVERIP;
|
extern std::string SHARDSERVERIP;
|
||||||
extern bool LOCALHOSTWORKAROUND;
|
extern bool LOCALHOSTWORKAROUND;
|
||||||
extern bool ANTICHEAT;
|
|
||||||
extern time_t TIMEOUT;
|
extern time_t TIMEOUT;
|
||||||
extern int VIEWDISTANCE;
|
extern int VIEWDISTANCE;
|
||||||
extern bool SIMULATEMOBS;
|
extern bool SIMULATEMOBS;
|
||||||
|
2
tdata
2
tdata
@ -1 +1 @@
|
|||||||
Subproject commit cc65dbb402b5baa2b604ed66132edd88cc82a52a
|
Subproject commit 8230fb8649d6da8de71e918c24efbb55d1c08a88
|
Loading…
Reference in New Issue
Block a user