mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-22 05:27:19 +00:00
Merge branch 'main' into fix--remove-the-wacrimes-from-our-docker-config
This commit is contained in:
commit
f7664ad838
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.18)
|
|||||||
project(Darkflame)
|
project(Darkflame)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
# Read variables from file
|
# Read variables from file
|
||||||
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <csignal>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ namespace Game {
|
|||||||
Logger* logger = nullptr;
|
Logger* logger = nullptr;
|
||||||
dServer* server = nullptr;
|
dServer* server = nullptr;
|
||||||
dConfig* config = nullptr;
|
dConfig* config = nullptr;
|
||||||
bool shouldShutdown = false;
|
Game::signal_t lastSignal = 0;
|
||||||
std::mt19937 randomEngine;
|
std::mt19937 randomEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +43,9 @@ int main(int argc, char** argv) {
|
|||||||
Diagnostics::SetProcessFileName(argv[0]);
|
Diagnostics::SetProcessFileName(argv[0]);
|
||||||
Diagnostics::Initialize();
|
Diagnostics::Initialize();
|
||||||
|
|
||||||
|
std::signal(SIGINT, Game::OnSignal);
|
||||||
|
std::signal(SIGTERM, Game::OnSignal);
|
||||||
|
|
||||||
//Create all the objects we need to run our service:
|
//Create all the objects we need to run our service:
|
||||||
Game::logger = SetupLogger();
|
Game::logger = SetupLogger();
|
||||||
if (!Game::logger) return EXIT_FAILURE;
|
if (!Game::logger) return EXIT_FAILURE;
|
||||||
@ -74,6 +78,7 @@ int main(int argc, char** argv) {
|
|||||||
masterIP = masterInfo->ip;
|
masterIP = masterInfo->ip;
|
||||||
masterPort = masterInfo->port;
|
masterPort = masterInfo->port;
|
||||||
}
|
}
|
||||||
|
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
|
||||||
|
|
||||||
Game::randomEngine = std::mt19937(time(0));
|
Game::randomEngine = std::mt19937(time(0));
|
||||||
|
|
||||||
@ -83,7 +88,7 @@ int main(int argc, char** argv) {
|
|||||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
|
|
||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
|
||||||
|
|
||||||
//Run it until server gets a kill message from Master:
|
//Run it until server gets a kill message from Master:
|
||||||
auto t = std::chrono::high_resolution_clock::now();
|
auto t = std::chrono::high_resolution_clock::now();
|
||||||
@ -96,13 +101,16 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
AuthPackets::LoadClaimCodes();
|
AuthPackets::LoadClaimCodes();
|
||||||
|
|
||||||
while (!Game::shouldShutdown) {
|
Game::logger->Flush(); // once immediately before main loop
|
||||||
|
while (!Game::ShouldShutdown()) {
|
||||||
//Check if we're still connected to master:
|
//Check if we're still connected to master:
|
||||||
if (!Game::server->GetIsConnectedToMaster()) {
|
if (!Game::server->GetIsConnectedToMaster()) {
|
||||||
framesSinceMasterDisconnect++;
|
framesSinceMasterDisconnect++;
|
||||||
|
|
||||||
if (framesSinceMasterDisconnect >= authFramerate)
|
if (framesSinceMasterDisconnect >= authFramerate) {
|
||||||
|
LOG("No connection to master!");
|
||||||
break; //Exit our loop, shut down.
|
break; //Exit our loop, shut down.
|
||||||
|
}
|
||||||
} else framesSinceMasterDisconnect = 0;
|
} else framesSinceMasterDisconnect = 0;
|
||||||
|
|
||||||
//In world we'd update our other systems here.
|
//In world we'd update our other systems here.
|
||||||
@ -141,6 +149,7 @@ int main(int argc, char** argv) {
|
|||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
|
||||||
//Delete our objects here:
|
//Delete our objects here:
|
||||||
Database::Destroy("AuthServer");
|
Database::Destroy("AuthServer");
|
||||||
delete Game::server;
|
delete Game::server;
|
||||||
|
@ -33,7 +33,7 @@ namespace Game {
|
|||||||
dConfig* config = nullptr;
|
dConfig* config = nullptr;
|
||||||
dChatFilter* chatFilter = nullptr;
|
dChatFilter* chatFilter = nullptr;
|
||||||
AssetManager* assetManager = nullptr;
|
AssetManager* assetManager = nullptr;
|
||||||
bool shouldShutdown = false;
|
Game::signal_t lastSignal = 0;
|
||||||
std::mt19937 randomEngine;
|
std::mt19937 randomEngine;
|
||||||
PlayerContainer playerContainer;
|
PlayerContainer playerContainer;
|
||||||
}
|
}
|
||||||
@ -48,6 +48,9 @@ int main(int argc, char** argv) {
|
|||||||
Diagnostics::SetProcessFileName(argv[0]);
|
Diagnostics::SetProcessFileName(argv[0]);
|
||||||
Diagnostics::Initialize();
|
Diagnostics::Initialize();
|
||||||
|
|
||||||
|
std::signal(SIGINT, Game::OnSignal);
|
||||||
|
std::signal(SIGTERM, Game::OnSignal);
|
||||||
|
|
||||||
//Create all the objects we need to run our service:
|
//Create all the objects we need to run our service:
|
||||||
Game::logger = SetupLogger();
|
Game::logger = SetupLogger();
|
||||||
if (!Game::logger) return EXIT_FAILURE;
|
if (!Game::logger) return EXIT_FAILURE;
|
||||||
@ -101,7 +104,7 @@ int main(int argc, char** argv) {
|
|||||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||||
|
|
||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
|
||||||
|
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||||
|
|
||||||
@ -118,7 +121,8 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t framesSinceMasterDisconnect = 0;
|
uint32_t framesSinceMasterDisconnect = 0;
|
||||||
uint32_t framesSinceLastSQLPing = 0;
|
uint32_t framesSinceLastSQLPing = 0;
|
||||||
|
|
||||||
while (!Game::shouldShutdown) {
|
Game::logger->Flush(); // once immediately before main loop
|
||||||
|
while (!Game::ShouldShutdown()) {
|
||||||
//Check if we're still connected to master:
|
//Check if we're still connected to master:
|
||||||
if (!Game::server->GetIsConnectedToMaster()) {
|
if (!Game::server->GetIsConnectedToMaster()) {
|
||||||
framesSinceMasterDisconnect++;
|
framesSinceMasterDisconnect++;
|
||||||
|
@ -5,6 +5,7 @@ set(DCOMMON_SOURCES
|
|||||||
"dConfig.cpp"
|
"dConfig.cpp"
|
||||||
"Diagnostics.cpp"
|
"Diagnostics.cpp"
|
||||||
"Logger.cpp"
|
"Logger.cpp"
|
||||||
|
"Game.cpp"
|
||||||
"GeneralUtils.cpp"
|
"GeneralUtils.cpp"
|
||||||
"LDFFormat.cpp"
|
"LDFFormat.cpp"
|
||||||
"MD5.cpp"
|
"MD5.cpp"
|
||||||
|
7
dCommon/Game.cpp
Normal file
7
dCommon/Game.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
namespace Game {
|
||||||
|
void OnSignal(int signal) {
|
||||||
|
lastSignal = signal;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
class dServer;
|
class dServer;
|
||||||
class Logger;
|
class Logger;
|
||||||
@ -16,6 +17,7 @@ class dZoneManager;
|
|||||||
class PlayerContainer;
|
class PlayerContainer;
|
||||||
|
|
||||||
namespace Game {
|
namespace Game {
|
||||||
|
using signal_t = volatile std::sig_atomic_t;
|
||||||
extern Logger* logger;
|
extern Logger* logger;
|
||||||
extern dServer* server;
|
extern dServer* server;
|
||||||
extern InstanceManager* im;
|
extern InstanceManager* im;
|
||||||
@ -25,9 +27,14 @@ namespace Game {
|
|||||||
extern RakPeerInterface* chatServer;
|
extern RakPeerInterface* chatServer;
|
||||||
extern AssetManager* assetManager;
|
extern AssetManager* assetManager;
|
||||||
extern SystemAddress chatSysAddr;
|
extern SystemAddress chatSysAddr;
|
||||||
extern bool shouldShutdown;
|
extern signal_t lastSignal;
|
||||||
extern EntityManager* entityManager;
|
extern EntityManager* entityManager;
|
||||||
extern dZoneManager* zoneManager;
|
extern dZoneManager* zoneManager;
|
||||||
extern PlayerContainer playerContainer;
|
extern PlayerContainer playerContainer;
|
||||||
extern std::string projectVersion;
|
extern std::string projectVersion;
|
||||||
|
|
||||||
|
inline bool ShouldShutdown() {
|
||||||
|
return lastSignal != 0;
|
||||||
|
}
|
||||||
|
void OnSignal(int signal);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
Writer::~Writer() {
|
Writer::~Writer() {
|
||||||
|
// Flush before we close
|
||||||
|
Flush();
|
||||||
// Dont try to close stdcout...
|
// Dont try to close stdcout...
|
||||||
if (!m_Outfile || m_IsConsoleWriter) return;
|
if (!m_Outfile || m_IsConsoleWriter) return;
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Now that the name is ok, we can get an objectID from Master:
|
//Now that the name is ok, we can get an objectID from Master:
|
||||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
ObjectIDManager::Instance()->RequestPersistentID([=, this](uint32_t objectID) {
|
||||||
if (Database::Get()->GetCharacterInfo(objectID)) {
|
if (Database::Get()->GetCharacterInfo(objectID)) {
|
||||||
LOG("Character object id unavailable, check object_id_tracker!");
|
LOG("Character object id unavailable, check object_id_tracker!");
|
||||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||||
|
@ -33,7 +33,7 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo
|
|||||||
if (activityID > 0) m_ActivityID = activityID;
|
if (activityID > 0) m_ActivityID = activityID;
|
||||||
else m_ActivityID = parent->GetVar<int32_t>(u"activityID");
|
else m_ActivityID = parent->GetVar<int32_t>(u"activityID");
|
||||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
std::vector<CDActivities> activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||||
|
|
||||||
for (CDActivities activity : activities) {
|
for (CDActivities activity : activities) {
|
||||||
m_ActivityInfo = activity;
|
m_ActivityInfo = activity;
|
||||||
@ -93,7 +93,7 @@ void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIniti
|
|||||||
|
|
||||||
void ActivityComponent::ReloadConfig() {
|
void ActivityComponent::ReloadConfig() {
|
||||||
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
|
||||||
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
std::vector<CDActivities> activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
|
||||||
for (auto activity : activities) {
|
for (auto activity : activities) {
|
||||||
auto mapID = m_ActivityInfo.instanceMapID;
|
auto mapID = m_ActivityInfo.instanceMapID;
|
||||||
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") {
|
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") {
|
||||||
@ -532,7 +532,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
|
|||||||
|
|
||||||
// First, get the activity data
|
// First, get the activity data
|
||||||
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
|
||||||
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
|
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([this](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
|
||||||
|
|
||||||
if (!activityRewards.empty()) {
|
if (!activityRewards.empty()) {
|
||||||
uint32_t minCoins = 0;
|
uint32_t minCoins = 0;
|
||||||
|
@ -312,7 +312,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
|
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
|
||||||
vehicle->AddCallbackTimer(2.0f, [=]() {
|
vehicle->AddCallbackTimer(2.0f, [=, this]() {
|
||||||
if (!vehicle || !this->m_Parent) return;
|
if (!vehicle || !this->m_Parent) return;
|
||||||
GameMessages::SendRacingResetPlayerToLastReset(
|
GameMessages::SendRacingResetPlayerToLastReset(
|
||||||
m_Parent->GetObjectID(), racingPlayer.playerID,
|
m_Parent->GetObjectID(), racingPlayer.playerID,
|
||||||
|
@ -249,7 +249,7 @@ bool Item::IsEquipped() const {
|
|||||||
bool Item::Consume() {
|
bool Item::Consume() {
|
||||||
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
|
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
|
||||||
|
|
||||||
auto skills = skillsTable->Query([=](const CDObjectSkills entry) {
|
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
|
||||||
return entry.objectTemplate == static_cast<uint32_t>(lot);
|
return entry.objectTemplate == static_cast<uint32_t>(lot);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -294,21 +294,20 @@ void VanityUtilities::ParseXML(const std::string& file) {
|
|||||||
auto* partyPhrases = npcs->FirstChildElement("partyphrases");
|
auto* partyPhrases = npcs->FirstChildElement("partyphrases");
|
||||||
|
|
||||||
if (partyPhrases == nullptr) {
|
if (partyPhrases == nullptr) {
|
||||||
LOG("Failed to parse party phrases");
|
LOG("No party phrases found");
|
||||||
return;
|
} else {
|
||||||
}
|
for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr;
|
||||||
|
phrase = phrase->NextSiblingElement("phrase")) {
|
||||||
for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr;
|
// Get the phrase
|
||||||
phrase = phrase->NextSiblingElement("phrase")) {
|
auto* text = phrase->GetText();
|
||||||
// Get the phrase
|
|
||||||
auto* text = phrase->GetText();
|
if (text == nullptr) {
|
||||||
|
LOG("Failed to parse party phrase");
|
||||||
if (text == nullptr) {
|
continue;
|
||||||
LOG("Failed to parse party phrase");
|
}
|
||||||
continue;
|
|
||||||
|
m_PartyPhrases.push_back(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_PartyPhrases.push_back(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* npc = npcs->FirstChildElement("npc"); npc != nullptr; npc = npc->NextSiblingElement("npc")) {
|
for (auto* npc = npcs->FirstChildElement("npc"); npc != nullptr; npc = npc->NextSiblingElement("npc")) {
|
||||||
|
@ -134,7 +134,7 @@ void InstanceManager::RemoveInstance(Instance* instance) {
|
|||||||
if (m_Instances[i] == instance) {
|
if (m_Instances[i] == instance) {
|
||||||
instance->SetShutdownComplete(true);
|
instance->SetShutdownComplete(true);
|
||||||
|
|
||||||
if (!Game::shouldShutdown) RedirectPendingRequests(instance);
|
if (!Game::ShouldShutdown()) RedirectPendingRequests(instance);
|
||||||
|
|
||||||
delete m_Instances[i];
|
delete m_Instances[i];
|
||||||
|
|
||||||
|
@ -47,12 +47,13 @@ namespace Game {
|
|||||||
InstanceManager* im = nullptr;
|
InstanceManager* im = nullptr;
|
||||||
dConfig* config = nullptr;
|
dConfig* config = nullptr;
|
||||||
AssetManager* assetManager = nullptr;
|
AssetManager* assetManager = nullptr;
|
||||||
bool shouldShutdown = false;
|
Game::signal_t lastSignal = 0;
|
||||||
|
bool universeShutdownRequested = false;
|
||||||
std::mt19937 randomEngine;
|
std::mt19937 randomEngine;
|
||||||
} //namespace Game
|
} //namespace Game
|
||||||
|
|
||||||
bool shutdownSequenceStarted = false;
|
bool shutdownSequenceStarted = false;
|
||||||
void ShutdownSequence(int32_t signal = -1);
|
int ShutdownSequence(int32_t signal = -1);
|
||||||
int32_t FinalizeShutdown(int32_t signal = -1);
|
int32_t FinalizeShutdown(int32_t signal = -1);
|
||||||
Logger* SetupLogger();
|
Logger* SetupLogger();
|
||||||
void HandlePacket(Packet* packet);
|
void HandlePacket(Packet* packet);
|
||||||
@ -73,8 +74,8 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//Triggers the shutdown sequence at application exit
|
//Triggers the shutdown sequence at application exit
|
||||||
std::atexit([]() { ShutdownSequence(); });
|
std::atexit([]() { ShutdownSequence(); });
|
||||||
signal(SIGINT, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
|
std::signal(SIGINT, Game::OnSignal);
|
||||||
signal(SIGTERM, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
|
std::signal(SIGTERM, Game::OnSignal);
|
||||||
|
|
||||||
//Create all the objects we need to run our service:
|
//Create all the objects we need to run our service:
|
||||||
Game::logger = SetupLogger();
|
Game::logger = SetupLogger();
|
||||||
@ -286,7 +287,7 @@ int main(int argc, char** argv) {
|
|||||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||||
if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port"));
|
if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port"));
|
||||||
|
|
||||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown);
|
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal);
|
||||||
|
|
||||||
//Query for the database for a server labeled "master"
|
//Query for the database for a server labeled "master"
|
||||||
|
|
||||||
@ -321,7 +322,8 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t framesSinceLastSQLPing = 0;
|
uint32_t framesSinceLastSQLPing = 0;
|
||||||
uint32_t framesSinceKillUniverseCommand = 0;
|
uint32_t framesSinceKillUniverseCommand = 0;
|
||||||
|
|
||||||
while (true) {
|
Game::logger->Flush();
|
||||||
|
while (!Game::ShouldShutdown()) {
|
||||||
//In world we'd update our other systems here.
|
//In world we'd update our other systems here.
|
||||||
|
|
||||||
//Check for packets here:
|
//Check for packets here:
|
||||||
@ -355,10 +357,10 @@ int main(int argc, char** argv) {
|
|||||||
framesSinceLastSQLPing++;
|
framesSinceLastSQLPing++;
|
||||||
|
|
||||||
//10m shutdown for universe kill command
|
//10m shutdown for universe kill command
|
||||||
if (Game::shouldShutdown) {
|
if (Game::universeShutdownRequested) {
|
||||||
if (framesSinceKillUniverseCommand >= shutdownUniverseTime) {
|
if (framesSinceKillUniverseCommand >= shutdownUniverseTime) {
|
||||||
//Break main loop and exit
|
//Break main loop and exit
|
||||||
break;
|
Game::lastSignal = -1;
|
||||||
} else
|
} else
|
||||||
framesSinceKillUniverseCommand++;
|
framesSinceKillUniverseCommand++;
|
||||||
}
|
}
|
||||||
@ -402,7 +404,7 @@ int main(int argc, char** argv) {
|
|||||||
t += std::chrono::milliseconds(masterFrameDelta);
|
t += std::chrono::milliseconds(masterFrameDelta);
|
||||||
std::this_thread::sleep_until(t);
|
std::this_thread::sleep_until(t);
|
||||||
}
|
}
|
||||||
return FinalizeShutdown(EXIT_SUCCESS);
|
return ShutdownSequence(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger* SetupLogger() {
|
Logger* SetupLogger() {
|
||||||
@ -799,7 +801,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
|
|
||||||
case eMasterMessageType::SHUTDOWN_UNIVERSE: {
|
case eMasterMessageType::SHUTDOWN_UNIVERSE: {
|
||||||
LOG("Received shutdown universe command, shutting down in 10 minutes.");
|
LOG("Received shutdown universe command, shutting down in 10 minutes.");
|
||||||
Game::shouldShutdown = true;
|
Game::universeShutdownRequested = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,9 +811,11 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownSequence(int32_t signal) {
|
int ShutdownSequence(int32_t signal) {
|
||||||
|
LOG("Recieved Signal %d", signal);
|
||||||
if (shutdownSequenceStarted) {
|
if (shutdownSequenceStarted) {
|
||||||
return;
|
LOG("Duplicate Shutdown Sequence");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Game::im) {
|
if (!Game::im) {
|
||||||
@ -820,7 +824,7 @@ void ShutdownSequence(int32_t signal) {
|
|||||||
|
|
||||||
Game::im->SetIsShuttingDown(true);
|
Game::im->SetIsShuttingDown(true);
|
||||||
shutdownSequenceStarted = true;
|
shutdownSequenceStarted = true;
|
||||||
Game::shouldShutdown = true;
|
Game::lastSignal = -1;
|
||||||
|
|
||||||
{
|
{
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
@ -889,7 +893,7 @@ void ShutdownSequence(int32_t signal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FinalizeShutdown(signal);
|
return FinalizeShutdown(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t FinalizeShutdown(int32_t signal) {
|
int32_t FinalizeShutdown(int32_t signal) {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
|
|
||||||
void StartChatServer() {
|
void StartChatServer() {
|
||||||
if (Game::shouldShutdown) {
|
if (Game::ShouldShutdown()) {
|
||||||
LOG("Currently shutting down. Chat will not be restarted.");
|
LOG("Currently shutting down. Chat will not be restarted.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -24,7 +24,7 @@ void StartChatServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StartAuthServer() {
|
void StartAuthServer() {
|
||||||
if (Game::shouldShutdown) {
|
if (Game::ShouldShutdown()) {
|
||||||
LOG("Currently shutting down. Auth will not be restarted.");
|
LOG("Currently shutting down. Auth will not be restarted.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,9 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
|
|||||||
RakNet::BitStream bitStream;
|
RakNet::BitStream bitStream;
|
||||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM);
|
BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM);
|
||||||
uint32_t netVersion;
|
uint32_t netVersion;
|
||||||
if (!GeneralUtils::TryParse(Game::config->GetValue("client_net_version"), netVersion)) {
|
const std::string& expectedVersion = Game::config->GetValue("client_net_version");
|
||||||
|
LOG("Expected Version: '%s'", expectedVersion.c_str());
|
||||||
|
if (!GeneralUtils::TryParse(expectedVersion, netVersion)) {
|
||||||
LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort);
|
LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public:
|
|||||||
}
|
}
|
||||||
} ReceiveDownloadCompleteCB;
|
} ReceiveDownloadCompleteCB;
|
||||||
|
|
||||||
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool* shouldShutdown, unsigned int zoneID) {
|
dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) {
|
||||||
mIP = ip;
|
mIP = ip;
|
||||||
mPort = port;
|
mPort = port;
|
||||||
mZoneID = zoneID;
|
mZoneID = zoneID;
|
||||||
@ -55,7 +55,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
|
|||||||
mReplicaManager = nullptr;
|
mReplicaManager = nullptr;
|
||||||
mServerType = serverType;
|
mServerType = serverType;
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
mShouldShutdown = shouldShutdown;
|
mShouldShutdown = lastSignal;
|
||||||
//Attempt to start our server here:
|
//Attempt to start our server here:
|
||||||
mIsOkay = Startup();
|
mIsOkay = Startup();
|
||||||
|
|
||||||
@ -75,7 +75,9 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
|
|||||||
//Connect to master if we are not master:
|
//Connect to master if we are not master:
|
||||||
if (serverType != ServerType::Master) {
|
if (serverType != ServerType::Master) {
|
||||||
SetupForMasterConnection();
|
SetupForMasterConnection();
|
||||||
ConnectToMaster();
|
if (!ConnectToMaster()) {
|
||||||
|
LOG("Failed ConnectToMaster!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set up Replica if we're a world server:
|
//Set up Replica if we're a world server:
|
||||||
@ -129,7 +131,7 @@ Packet* dServer::ReceiveFromMaster() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eMasterMessageType::SHUTDOWN:
|
case eMasterMessageType::SHUTDOWN:
|
||||||
*mShouldShutdown = true;
|
*mShouldShutdown = -2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//When we handle these packets in World instead dServer, we just return the packet's pointer.
|
//When we handle these packets in World instead dServer, we just return the packet's pointer.
|
||||||
@ -236,10 +238,12 @@ void dServer::Shutdown() {
|
|||||||
void dServer::SetupForMasterConnection() {
|
void dServer::SetupForMasterConnection() {
|
||||||
mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0);
|
mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0);
|
||||||
mMasterPeer = RakNetworkFactory::GetRakPeerInterface();
|
mMasterPeer = RakNetworkFactory::GetRakPeerInterface();
|
||||||
mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
|
bool ret = mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1);
|
||||||
|
if (!ret) LOG("Failed MasterPeer Startup!");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dServer::ConnectToMaster() {
|
bool dServer::ConnectToMaster() {
|
||||||
|
//LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort);
|
||||||
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
|
return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <csignal>
|
||||||
#include "RakPeerInterface.h"
|
#include "RakPeerInterface.h"
|
||||||
#include "ReplicaManager.h"
|
#include "ReplicaManager.h"
|
||||||
#include "NetworkIDManager.h"
|
#include "NetworkIDManager.h"
|
||||||
@ -15,6 +16,10 @@ enum class ServerType : uint32_t {
|
|||||||
World
|
World
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace Game {
|
||||||
|
using signal_t = volatile std::sig_atomic_t;
|
||||||
|
}
|
||||||
|
|
||||||
class dServer {
|
class dServer {
|
||||||
public:
|
public:
|
||||||
// Default constructor should only used for testing!
|
// Default constructor should only used for testing!
|
||||||
@ -31,7 +36,7 @@ public:
|
|||||||
int masterPort,
|
int masterPort,
|
||||||
ServerType serverType,
|
ServerType serverType,
|
||||||
dConfig* config,
|
dConfig* config,
|
||||||
bool* shouldShutdown,
|
Game::signal_t* shouldShutdown,
|
||||||
unsigned int zoneID = 0);
|
unsigned int zoneID = 0);
|
||||||
~dServer();
|
~dServer();
|
||||||
|
|
||||||
@ -81,9 +86,9 @@ private:
|
|||||||
NetworkIDManager* mNetIDManager = nullptr;
|
NetworkIDManager* mNetIDManager = nullptr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not to shut down the server. Pointer to Game::shouldShutdown.
|
* Whether or not to shut down the server. Pointer to Game::lastSignal.
|
||||||
*/
|
*/
|
||||||
bool* mShouldShutdown = nullptr;
|
Game::signal_t* mShouldShutdown = nullptr;
|
||||||
SocketDescriptor mSocketDescriptor;
|
SocketDescriptor mSocketDescriptor;
|
||||||
std::string mIP;
|
std::string mIP;
|
||||||
int mPort;
|
int mPort;
|
||||||
|
@ -88,7 +88,7 @@ namespace Game {
|
|||||||
RakPeerInterface* chatServer = nullptr;
|
RakPeerInterface* chatServer = nullptr;
|
||||||
std::mt19937 randomEngine;
|
std::mt19937 randomEngine;
|
||||||
SystemAddress chatSysAddr;
|
SystemAddress chatSysAddr;
|
||||||
bool shouldShutdown = false;
|
Game::signal_t lastSignal = 0;
|
||||||
EntityManager* entityManager = nullptr;
|
EntityManager* entityManager = nullptr;
|
||||||
dZoneManager* zoneManager = nullptr;
|
dZoneManager* zoneManager = nullptr;
|
||||||
std::string projectVersion = PROJECT_VERSION;
|
std::string projectVersion = PROJECT_VERSION;
|
||||||
@ -124,8 +124,8 @@ int main(int argc, char** argv) {
|
|||||||
// Triggers the shutdown sequence at application exit
|
// Triggers the shutdown sequence at application exit
|
||||||
std::atexit(WorldShutdownSequence);
|
std::atexit(WorldShutdownSequence);
|
||||||
|
|
||||||
signal(SIGINT, [](int) { WorldShutdownSequence(); });
|
std::signal(SIGINT, Game::OnSignal);
|
||||||
signal(SIGTERM, [](int) { WorldShutdownSequence(); });
|
std::signal(SIGTERM, Game::OnSignal);
|
||||||
|
|
||||||
uint32_t zoneID = 1000;
|
uint32_t zoneID = 1000;
|
||||||
uint32_t cloneID = 0;
|
uint32_t cloneID = 0;
|
||||||
@ -212,7 +212,7 @@ int main(int argc, char** argv) {
|
|||||||
UserManager::Instance()->Initialize();
|
UserManager::Instance()->Initialize();
|
||||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||||
|
|
||||||
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
|
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID);
|
||||||
|
|
||||||
//Connect to the chat server:
|
//Connect to the chat server:
|
||||||
uint32_t chatPort = 1501;
|
uint32_t chatPort = 1501;
|
||||||
@ -313,6 +313,8 @@ int main(int argc, char** argv) {
|
|||||||
uint32_t saveTime = 10 * 60 * currentFramerate; // 10 minutes in frames
|
uint32_t saveTime = 10 * 60 * currentFramerate; // 10 minutes in frames
|
||||||
uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames
|
uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames
|
||||||
uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others.
|
uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others.
|
||||||
|
|
||||||
|
Game::logger->Flush(); // once immediately before the main loop
|
||||||
while (true) {
|
while (true) {
|
||||||
Metrics::StartMeasurement(MetricVariable::Frame);
|
Metrics::StartMeasurement(MetricVariable::Frame);
|
||||||
Metrics::StartMeasurement(MetricVariable::GameLoop);
|
Metrics::StartMeasurement(MetricVariable::GameLoop);
|
||||||
@ -363,9 +365,9 @@ int main(int argc, char** argv) {
|
|||||||
if (!Game::server->GetIsConnectedToMaster()) {
|
if (!Game::server->GetIsConnectedToMaster()) {
|
||||||
framesSinceMasterDisconnect++;
|
framesSinceMasterDisconnect++;
|
||||||
|
|
||||||
if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::shouldShutdown) {
|
if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::ShouldShutdown()) {
|
||||||
LOG("Game loop running but no connection to master for %d frames, shutting down", noMasterConnectionTimeout);
|
LOG("Game loop running but no connection to master for %d frames, shutting down", noMasterConnectionTimeout);
|
||||||
Game::shouldShutdown = true;
|
Game::lastSignal = -1;
|
||||||
}
|
}
|
||||||
} else framesSinceMasterDisconnect = 0;
|
} else framesSinceMasterDisconnect = 0;
|
||||||
|
|
||||||
@ -460,7 +462,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
//If we haven't had any players for a while, time out and shut down:
|
//If we haven't had any players for a while, time out and shut down:
|
||||||
if (framesSinceLastUser >= emptyShutdownTime) {
|
if (framesSinceLastUser >= emptyShutdownTime) {
|
||||||
Game::shouldShutdown = true;
|
Game::lastSignal = -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
framesSinceLastUser = 0;
|
framesSinceLastUser = 0;
|
||||||
@ -513,7 +515,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Game::shouldShutdown && !worldShutdownSequenceComplete) {
|
if (Game::ShouldShutdown() && !worldShutdownSequenceComplete) {
|
||||||
WorldShutdownProcess(zoneID);
|
WorldShutdownProcess(zoneID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -822,7 +824,7 @@ void HandlePacket(Packet* packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case eMasterMessageType::SHUTDOWN: {
|
case eMasterMessageType::SHUTDOWN: {
|
||||||
Game::shouldShutdown = true;
|
Game::lastSignal = -1;
|
||||||
LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1289,9 +1291,9 @@ void WorldShutdownProcess(uint32_t zoneId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldShutdownSequence() {
|
void WorldShutdownSequence() {
|
||||||
Game::shouldShutdown = true;
|
Game::lastSignal = -1;
|
||||||
#ifndef DARKFLAME_PLATFORM_WIN32
|
#ifndef DARKFLAME_PLATFORM_WIN32
|
||||||
if (Game::shouldShutdown || worldShutdownSequenceComplete)
|
if (Game::ShouldShutdown() || worldShutdownSequenceComplete)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -51,20 +51,20 @@ Spawner::Spawner(const SpawnerInfo info) {
|
|||||||
std::vector<Spawner*> spawnSmashSpawnersN = Game::zoneManager->GetSpawnersByName(m_Info.spawnOnSmashGroupName);
|
std::vector<Spawner*> spawnSmashSpawnersN = Game::zoneManager->GetSpawnersByName(m_Info.spawnOnSmashGroupName);
|
||||||
for (Entity* ssEntity : spawnSmashEntities) {
|
for (Entity* ssEntity : spawnSmashEntities) {
|
||||||
m_SpawnSmashFoundGroup = true;
|
m_SpawnSmashFoundGroup = true;
|
||||||
ssEntity->AddDieCallback([=]() {
|
ssEntity->AddDieCallback([=, this]() {
|
||||||
Spawn();
|
Spawn();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (Spawner* ssSpawner : spawnSmashSpawners) {
|
for (Spawner* ssSpawner : spawnSmashSpawners) {
|
||||||
m_SpawnSmashFoundGroup = true;
|
m_SpawnSmashFoundGroup = true;
|
||||||
ssSpawner->AddSpawnedEntityDieCallback([=]() {
|
ssSpawner->AddSpawnedEntityDieCallback([=, this]() {
|
||||||
Spawn();
|
Spawn();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (Spawner* ssSpawner : spawnSmashSpawnersN) {
|
for (Spawner* ssSpawner : spawnSmashSpawnersN) {
|
||||||
m_SpawnSmashFoundGroup = true;
|
m_SpawnSmashFoundGroup = true;
|
||||||
m_SpawnOnSmash = ssSpawner;
|
m_SpawnOnSmash = ssSpawner;
|
||||||
ssSpawner->AddSpawnedEntityDieCallback([=]() {
|
ssSpawner->AddSpawnedEntityDieCallback([=, this]() {
|
||||||
Spawn();
|
Spawn();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ TEST_F(EncodingTest, TestEncodingHello) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(EncodingTest, TestEncodingUmlaut) {
|
TEST_F(EncodingTest, TestEncodingUmlaut) {
|
||||||
originalWord = u8"Frühling";
|
originalWord = reinterpret_cast<const char*>(u8"Frühling");
|
||||||
originalWordSv = originalWord;
|
originalWordSv = originalWord;
|
||||||
|
|
||||||
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F');
|
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F');
|
||||||
|
2
thirdparty/raknet/CMakeLists.txt
vendored
2
thirdparty/raknet/CMakeLists.txt
vendored
@ -80,6 +80,8 @@ target_compile_options(raknet PRIVATE
|
|||||||
$<$<CXX_COMPILER_ID:MSVC>:
|
$<$<CXX_COMPILER_ID:MSVC>:
|
||||||
/w>)
|
/w>)
|
||||||
|
|
||||||
|
set_property(TARGET raknet PROPERTY CXX_STANDARD 17)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Link Win Sockets 2 to RakNet
|
# Link Win Sockets 2 to RakNet
|
||||||
target_link_libraries(raknet ws2_32)
|
target_link_libraries(raknet ws2_32)
|
||||||
|
Loading…
Reference in New Issue
Block a user