mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-10-04 05:30:06 +00:00
inital commit
This commit is contained in:
244
src/CNLoginServer.cpp
Normal file
244
src/CNLoginServer.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "CNLoginServer.hpp"
|
||||
#include "CNShared.hpp"
|
||||
#include "CNStructs.hpp"
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
/*
|
||||
This is *not* connected to any database, so i'm sending dummy data just to get the client to work & connect to the shard <3
|
||||
*/
|
||||
|
||||
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
|
||||
|
||||
CNLoginServer::CNLoginServer(uint16_t p) {
|
||||
port = p;
|
||||
pHandler = &CNLoginServer::handlePacket;
|
||||
init();
|
||||
}
|
||||
|
||||
void CNLoginServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
||||
switch (data->type) {
|
||||
case P_CL2LS_REQ_LOGIN: {
|
||||
sP_CL2LS_REQ_LOGIN* login = (sP_CL2LS_REQ_LOGIN*)data->buf;
|
||||
sP_LS2CL_REP_LOGIN_SUCC* response = (sP_LS2CL_REP_LOGIN_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_LOGIN_SUCC));
|
||||
uint64_t cachedKey = sock->getEKey(); // so we can still send the response packet with the correct key
|
||||
int charCount = 2; // send 4 randomly generated characters for now
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_LOGIN:" << std::endl;
|
||||
std::cout << "\tClient version: " << login->iClientVerA << "." << login->iClientVerB << "." << login->iClientVerC << std::endl;
|
||||
std::cout << "\tLogin type: " << login->iLoginType << std::endl;
|
||||
std::cout << "\tID: " << U16toU8(login->szID) << " Password: " << U16toU8(login->szPassword) << std::endl;
|
||||
)
|
||||
|
||||
response->iCharCount = charCount;
|
||||
response->iSlotNum = 1;
|
||||
response->iPaymentFlag = 1;
|
||||
response->iOpenBetaFlag = 0;
|
||||
response->uiSvrTime = getTime();
|
||||
|
||||
// set username in response packet
|
||||
memcpy(response->szID, login->szID, sizeof(char16_t) * 33);
|
||||
|
||||
// update keys
|
||||
sock->setEKey(CNSocketEncryption::createNewKey(response->uiSvrTime, response->iCharCount + 1, response->iSlotNum + 1));
|
||||
sock->setFEKey(CNSocketEncryption::createNewKey((uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]), login->iClientVerC, 1));
|
||||
|
||||
// send the response in with original key
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_LOGIN_SUCC, sizeof(sP_LS2CL_REP_LOGIN_SUCC), cachedKey));
|
||||
|
||||
loginSessions[sock] = CNLoginData();
|
||||
|
||||
if (settings::LOGINRANDCHARACTERS) {
|
||||
// now send the characters :)
|
||||
for (int i = 0; i < charCount; i++) {
|
||||
sP_LS2CL_REP_CHAR_INFO* charInfo = (sP_LS2CL_REP_CHAR_INFO*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_INFO));
|
||||
charInfo->iSlot = (int8_t)i + 1;
|
||||
charInfo->iLevel = (int16_t)1;
|
||||
charInfo->sPC_Style.iPC_UID = rand(); // unique identifier for the character
|
||||
charInfo->sPC_Style.iNameCheck = 1;
|
||||
charInfo->sPC_Style.iGender = (i%2)+1; // can be 1(boy) or 2(girl)
|
||||
charInfo->sPC_Style.iFaceStyle = 1;
|
||||
charInfo->sPC_Style.iHairStyle = 1;
|
||||
charInfo->sPC_Style.iHairColor = (rand()%19) + 1; // 1 - 19
|
||||
charInfo->sPC_Style.iSkinColor = (rand()%13) + 1; // 1 - 13
|
||||
charInfo->sPC_Style.iEyeColor = (rand()%6) + 1; // 1 - 6
|
||||
charInfo->sPC_Style.iHeight = (rand()%6); // 0 -5
|
||||
charInfo->sPC_Style.iBody = (rand()%4); // 0 - 3
|
||||
charInfo->sPC_Style.iClass = 0;
|
||||
charInfo->sPC_Style2 = sPCStyle2(1, 1, 1);
|
||||
|
||||
// past's town hall
|
||||
charInfo->iX = settings::SPAWN_X;
|
||||
charInfo->iY = settings::SPAWN_Y;
|
||||
charInfo->iZ = settings::SPAWN_Z;
|
||||
|
||||
U8toU16(std::string("Player"), charInfo->sPC_Style.szFirstName);
|
||||
U8toU16(std::to_string(i + 1), charInfo->sPC_Style.szLastName);
|
||||
|
||||
int64_t UID = charInfo->sPC_Style.iPC_UID;
|
||||
loginSessions[sock].characters[UID] = Player();
|
||||
loginSessions[sock].characters[UID].level = charInfo->iLevel;
|
||||
loginSessions[sock].characters[UID].slot = charInfo->iSlot;
|
||||
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
|
||||
loginSessions[sock].characters[UID].x = charInfo->iX;
|
||||
loginSessions[sock].characters[UID].y = charInfo->iY;
|
||||
loginSessions[sock].characters[UID].z = charInfo->iZ;
|
||||
loginSessions[sock].characters[UID].PCStyle = charInfo->sPC_Style;
|
||||
loginSessions[sock].characters[UID].PCStyle2 = charInfo->sPC_Style2;
|
||||
|
||||
for (int i = 0; i < AEQUIP_COUNT; i++) {
|
||||
// setup item
|
||||
charInfo->aEquip[i].iID = 0;
|
||||
charInfo->aEquip[i].iType = i;
|
||||
charInfo->aEquip[i].iOpt = 0;
|
||||
loginSessions[sock].characters[UID].Equip[i] = charInfo->aEquip[i];
|
||||
}
|
||||
|
||||
// set default to the first character
|
||||
if (i == 0)
|
||||
loginSessions[sock].selectedChar = UID;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)charInfo, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO), sock->getEKey()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REP_LIVE_CHECK: {
|
||||
// stubbed, the client really doesn't care LOL
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REQ_CHECK_CHAR_NAME: {
|
||||
// naughty words allowed!!!!!!!! (also for some reason, the client will always show 'Player 0' if you manually type a name. It will show up for other connected players though)
|
||||
sP_CL2LS_REQ_CHECK_CHAR_NAME* nameCheck = (sP_CL2LS_REQ_CHECK_CHAR_NAME*)data->buf;
|
||||
sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC* response = (sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_CHECK_CHAR_NAME:" << std::endl;
|
||||
std::cout << "\tFirstName: " << U16toU8(nameCheck->szFirstName) << " LastName: " << U16toU8(nameCheck->szLastName) << std::endl;
|
||||
)
|
||||
|
||||
memcpy(response->szFirstName, nameCheck->szFirstName, sizeof(char16_t) * 9);
|
||||
memcpy(response->szLastName, nameCheck->szLastName, sizeof(char16_t) * 17);
|
||||
|
||||
// fr*ck allowed!!!
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHECK_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC), sock->getEKey()));
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REQ_SAVE_CHAR_NAME: {
|
||||
sP_CL2LS_REQ_SAVE_CHAR_NAME* save = (sP_CL2LS_REQ_SAVE_CHAR_NAME*)data->buf;
|
||||
sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC* response = (sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_SAVE_CHAR_NAME:" << std::endl;
|
||||
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
|
||||
std::cout << "\tGender: " << (int)save->iGender << std::endl;
|
||||
std::cout << "\tName: " << U16toU8(save->szFirstName) << " " << U16toU8(save->szLastName) << std::endl;
|
||||
)
|
||||
|
||||
response->iSlotNum = save->iSlotNum;
|
||||
response->iGender = save->iGender;
|
||||
memcpy(response->szFirstName, save->szFirstName, sizeof(char16_t) * 9);
|
||||
memcpy(response->szLastName, save->szLastName, sizeof(char16_t) * 17);
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_SAVE_CHAR_NAME_SUCC, sizeof(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC), sock->getEKey()));
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REQ_CHAR_CREATE: {
|
||||
sP_CL2LS_REQ_CHAR_CREATE* character = (sP_CL2LS_REQ_CHAR_CREATE*)data->buf;
|
||||
sP_LS2CL_REP_CHAR_CREATE_SUCC* response = (sP_LS2CL_REP_CHAR_CREATE_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_CHAR_CREATE:" << std::endl;
|
||||
std::cout << "\tPC_UID: " << character->PCStyle.iPC_UID << std::endl;
|
||||
std::cout << "\tNameCheck: " << (int)character->PCStyle.iNameCheck << std::endl;
|
||||
std::cout << "\tName: " << U16toU8(character->PCStyle.szFirstName) << " " << U16toU8(character->PCStyle.szLastName) << std::endl;
|
||||
std::cout << "\tGender: " << (int)character->PCStyle.iGender << std::endl;
|
||||
std::cout << "\tFace: " << (int)character->PCStyle.iFaceStyle << std::endl;
|
||||
std::cout << "\tHair: " << (int)character->PCStyle.iHairStyle << std::endl;
|
||||
std::cout << "\tHair Color: " << (int)character->PCStyle.iHairColor << std::endl;
|
||||
std::cout << "\tSkin Color: " << (int)character->PCStyle.iSkinColor << std::endl;
|
||||
std::cout << "\tEye Color: " << (int)character->PCStyle.iEyeColor << std::endl;
|
||||
std::cout << "\tHeight: " << (int)character->PCStyle.iHeight << std::endl;
|
||||
std::cout << "\tBody: " << (int)character->PCStyle.iBody << std::endl;
|
||||
std::cout << "\tClass: " << (int)character->PCStyle.iClass << std::endl;
|
||||
std::cout << "\tiEquipUBID: " << (int)character->sOn_Item.iEquipUBID << std::endl;
|
||||
std::cout << "\tiEquipLBID: " << (int)character->sOn_Item.iEquipLBID << std::endl;
|
||||
std::cout << "\tiEquipFootID: " << (int)character->sOn_Item.iEquipFootID << std::endl;
|
||||
)
|
||||
|
||||
character->PCStyle.iNameCheck = 1;
|
||||
response->PC_Style = character->PCStyle;
|
||||
response->PC_Style2 = sPCStyle2(1, 1, 1);
|
||||
response->iLevel = 1;
|
||||
response->sOn_Item = character->sOn_Item;
|
||||
|
||||
int64_t UID = character->PCStyle.iPC_UID;
|
||||
loginSessions[sock].characters[UID] = Player();
|
||||
loginSessions[sock].characters[UID].level = 1;
|
||||
loginSessions[sock].characters[UID].FEKey = sock->getFEKey();
|
||||
loginSessions[sock].characters[UID].PCStyle = character->PCStyle;
|
||||
loginSessions[sock].characters[UID].PCStyle2 = sPCStyle2(1, 0, 1);
|
||||
loginSessions[sock].characters[UID].x = settings::SPAWN_X;
|
||||
loginSessions[sock].characters[UID].y = settings::SPAWN_Y;
|
||||
loginSessions[sock].characters[UID].z = settings::SPAWN_Z;
|
||||
loginSessions[sock].characters[UID].Equip[1].iID = character->sOn_Item.iEquipUBID; // upper body
|
||||
loginSessions[sock].characters[UID].Equip[1].iType = 1;
|
||||
loginSessions[sock].characters[UID].Equip[2].iID = character->sOn_Item.iEquipLBID; // lower body
|
||||
loginSessions[sock].characters[UID].Equip[2].iType = 2;
|
||||
loginSessions[sock].characters[UID].Equip[3].iID = character->sOn_Item.iEquipFootID; // foot!
|
||||
loginSessions[sock].characters[UID].Equip[3].iType = 3;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHAR_CREATE_SUCC, sizeof(sP_LS2CL_REP_CHAR_CREATE_SUCC), sock->getEKey()));
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REQ_CHAR_SELECT: {
|
||||
// character selected
|
||||
sP_CL2LS_REQ_CHAR_SELECT* chararacter = (sP_CL2LS_REQ_CHAR_SELECT*)data->buf;
|
||||
sP_LS2CL_REP_CHAR_SELECT_SUCC* response = (sP_LS2CL_REP_CHAR_SELECT_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_CHAR_SELECT:" << std::endl;
|
||||
std::cout << "\tPC_UID: " << chararacter->iPC_UID << std::endl;
|
||||
)
|
||||
|
||||
loginSessions[sock].selectedChar = chararacter->iPC_UID;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_CHAR_SELECT_SUCC, sizeof(sP_LS2CL_REP_CHAR_SELECT_SUCC), sock->getEKey()));
|
||||
break;
|
||||
}
|
||||
case P_CL2LS_REQ_SHARD_SELECT: {
|
||||
// tell client to connect to the shard server
|
||||
sP_CL2LS_REQ_SHARD_SELECT* shard = (sP_CL2LS_REQ_SHARD_SELECT*)data->buf;
|
||||
sP_LS2CL_REP_SHARD_SELECT_SUCC* response = (sP_LS2CL_REP_SHARD_SELECT_SUCC*)xmalloc(sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2LS_REQ_SHARD_SELECT:" << std::endl;
|
||||
std::cout << "\tShard: " << (int)shard->iShardNum << std::endl;
|
||||
)
|
||||
|
||||
const char* SHARD_IP = settings::SHARDSERVERIP.c_str();
|
||||
response->iEnterSerialKey = rand();
|
||||
response->g_FE_ServerPort = settings::SHARDPORT;
|
||||
|
||||
// copy IP to response (this struct uses ASCII encoding so we don't have to goof around converting encodings)
|
||||
memcpy(response->g_FE_ServerIP, SHARD_IP, strlen(SHARD_IP));
|
||||
response->g_FE_ServerIP[strlen(SHARD_IP)] = '\0';
|
||||
|
||||
// pass player to CNSharedData
|
||||
CNSharedData::setPlayer(response->iEnterSerialKey, loginSessions[sock].characters[loginSessions[sock].selectedChar]);
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_LS2CL_REP_SHARD_SELECT_SUCC, sizeof(sP_LS2CL_REP_SHARD_SELECT_SUCC), sock->getEKey()));
|
||||
sock->kill(); // client should connect to the Shard server now
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::cerr << "OpenFusion: LOGIN UNIMPLM ERR. PacketType: " << data->type << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CNLoginServer::killConnection(CNSocket* cns) {
|
||||
loginSessions.erase(cns);
|
||||
}
|
49
src/CNLoginServer.hpp
Normal file
49
src/CNLoginServer.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _CNLS_HPP
|
||||
#define _CNLS_HPP
|
||||
|
||||
#include "CNProtocol.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
enum LOGINPACKETID {
|
||||
// client to login server
|
||||
P_CL2LS_REQ_LOGIN = 301989889,
|
||||
P_CL2LS_REQ_CHECK_CHAR_NAME = 301989890,
|
||||
P_CL2LS_REQ_SAVE_CHAR_NAME = 301989891,
|
||||
P_CL2LS_REQ_CHAR_CREATE = 301989892,
|
||||
P_CL2LS_REQ_CHAR_SELECT = 301989893,
|
||||
P_CL2LS_REQ_SHARD_SELECT = 301989895,
|
||||
P_CL2LS_REP_LIVE_CHECK = 301989900,
|
||||
P_CL2LS_REQ_SHARD_LIST_INFO = 301989896,
|
||||
|
||||
// login server 2 client
|
||||
P_LS2CL_REP_LOGIN_SUCC = 553648129,
|
||||
P_LS2CL_REP_CHAR_INFO = 553648131,
|
||||
P_LS2CL_REP_CHECK_CHAR_NAME_SUCC = 553648133,
|
||||
P_LS2CL_REP_SAVE_CHAR_NAME_SUCC = 553648135,
|
||||
P_LS2CL_REP_CHAR_CREATE_SUCC = 553648137,
|
||||
P_LS2CL_REP_CHAR_SELECT_SUCC = 553648139,
|
||||
P_LS2CL_REP_SHARD_SELECT_SUCC = 553648143,
|
||||
P_LS2CL_REQ_LIVE_CHECK = 553648150,
|
||||
P_LS2CL_REP_SHARD_LIST_INFO_SUCC = 553648153
|
||||
};
|
||||
|
||||
struct CNLoginData {
|
||||
std::map<int64_t, Player> characters;
|
||||
int64_t selectedChar;
|
||||
};
|
||||
|
||||
// WARNING: THERE CAN ONLY BE ONE OF THESE SERVERS AT A TIME!!!!!! TODO: change loginSessions & packet handlers to be non-static
|
||||
class CNLoginServer : public CNServer {
|
||||
private:
|
||||
static void handlePacket(CNSocket* sock, CNPacketData* data);
|
||||
static std::map<CNSocket*, CNLoginData> loginSessions;
|
||||
|
||||
public:
|
||||
CNLoginServer(uint16_t p);
|
||||
|
||||
void killConnection(CNSocket* cns);
|
||||
};
|
||||
|
||||
#endif
|
283
src/CNProtocol.cpp
Normal file
283
src/CNProtocol.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "CNProtocol.hpp"
|
||||
|
||||
// ========================================================[[ CNSocketEncryption ]]========================================================
|
||||
|
||||
// literally C/P from the client and converted to C++ (does some byte swapping /shrug)
|
||||
int CNSocketEncryption::Encrypt_byte_change_A(int ERSize, uint8_t* data, int size) {
|
||||
int num = 0;
|
||||
int num2 = 0;
|
||||
int num3 = 0;
|
||||
|
||||
while (num + ERSize <= size)
|
||||
{
|
||||
int num4 = num + num3;
|
||||
int num5 = num + (ERSize - 1 - num3);
|
||||
|
||||
uint8_t b = data[num4];
|
||||
data[num4] = data[num5];
|
||||
data[num5] = b;
|
||||
num += ERSize;
|
||||
num3++;
|
||||
if (num3 > ERSize / 2)
|
||||
{
|
||||
num3 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
num2 = ERSize - (num + ERSize - size);
|
||||
return num + num2;
|
||||
}
|
||||
|
||||
int CNSocketEncryption::xorData(uint8_t* buffer, uint8_t* key, int size) {
|
||||
// xor every 8 bytes with 8 byte key
|
||||
for (int i = 0; i < size; i++) {
|
||||
buffer[i] ^= key[i % keyLength];
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
uint64_t CNSocketEncryption::createNewKey(uint64_t uTime, int32_t iv1, int32_t iv2) {
|
||||
uint64_t num = (uint64_t)(iv1 + 1);
|
||||
uint64_t num2 = (uint64_t)(iv2 + 1);
|
||||
uint64_t dEKey = (uint64_t)(*(uint64_t*)&defaultKey[0]);
|
||||
return dEKey * (uTime * num * num2);
|
||||
}
|
||||
|
||||
int CNSocketEncryption::encryptData(uint8_t* buffer, uint8_t* key, int size) {
|
||||
int eRSize = size % (keyLength / 2 + 1) * 2 + keyLength; // C/P from client
|
||||
int size2 = xorData(buffer, key, size);
|
||||
return Encrypt_byte_change_A(eRSize, buffer, size2);
|
||||
}
|
||||
|
||||
int CNSocketEncryption::decryptData(uint8_t* buffer, uint8_t* key, int size) {
|
||||
int eRSize = size % (keyLength / 2 + 1) * 2 + keyLength; // size % of 18????
|
||||
int size2 = Encrypt_byte_change_A(eRSize, buffer, size);
|
||||
return xorData(buffer, key, size2);
|
||||
}
|
||||
|
||||
// ========================================================[[ CNPacketData ]]========================================================
|
||||
|
||||
CNPacketData::CNPacketData(void* b, uint32_t t, int l, uint64_t k): buf(b), type(t), size(l), key(k) {}
|
||||
|
||||
CNPacketData::~CNPacketData() {
|
||||
free(buf); // we own the buffer
|
||||
}
|
||||
|
||||
// ========================================================[[ CNSocket ]]========================================================
|
||||
|
||||
CNSocket::CNSocket(SOCKET s, PacketHandler ph): sock(s), pHandler(ph) {
|
||||
EKey = (uint64_t)(*(uint64_t*)&CNSocketEncryption::defaultKey[0]);
|
||||
}
|
||||
|
||||
bool CNSocket::sendData(uint8_t* data, int size) {
|
||||
int sentBytes = 0;
|
||||
|
||||
while (sentBytes < size) {
|
||||
int sent = send(sock, (buffer_t*)(data + sentBytes), size - sentBytes, 0); // no flags defined
|
||||
if (SOCKETERROR(sent))
|
||||
return false; // error occured while sending bytes
|
||||
sentBytes += sent;
|
||||
}
|
||||
|
||||
return true; // it worked!
|
||||
}
|
||||
|
||||
void CNSocket::setEKey(uint64_t k) {
|
||||
EKey = k;
|
||||
}
|
||||
|
||||
void CNSocket::setFEKey(uint64_t k) {
|
||||
FEKey = k;
|
||||
}
|
||||
|
||||
uint64_t CNSocket::getEKey() {
|
||||
return EKey;
|
||||
}
|
||||
|
||||
uint64_t CNSocket::getFEKey() {
|
||||
return FEKey;
|
||||
}
|
||||
|
||||
bool CNSocket::isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
void CNSocket::kill() {
|
||||
alive = false;
|
||||
#ifdef _WIN32
|
||||
shutdown(sock, SD_BOTH);
|
||||
closesocket(sock);
|
||||
#else
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CNSocket::sendPacket(CNPacketData* pak) {
|
||||
int tmpSize = pak->size + sizeof(uint32_t);
|
||||
uint8_t* tmpBuf = (uint8_t*)xmalloc(tmpSize);
|
||||
|
||||
// copy packet type to the front of the buffer & then the actual buffer
|
||||
memcpy(tmpBuf, (void*)&pak->type, sizeof(uint32_t));
|
||||
memcpy(tmpBuf+sizeof(uint32_t), pak->buf, pak->size);
|
||||
|
||||
// encrypt the packet
|
||||
CNSocketEncryption::encryptData((uint8_t*)tmpBuf, (uint8_t*)(&pak->key), tmpSize);
|
||||
|
||||
// send packet size
|
||||
sendData((uint8_t*)&tmpSize, sizeof(uint32_t));
|
||||
|
||||
// send packet data!
|
||||
sendData(tmpBuf, tmpSize);
|
||||
|
||||
delete pak;
|
||||
free(tmpBuf); // free tmp buffer
|
||||
}
|
||||
|
||||
void CNSocket::step() {
|
||||
if (readSize <= 0) {
|
||||
// we aren't reading a packet yet, try to start looking for one
|
||||
int recved = recv(sock, (buffer_t*)readBuffer, sizeof(int32_t), 0);
|
||||
if (!SOCKETERROR(recved)) {
|
||||
// we got out packet size!!!!
|
||||
readSize = *((int32_t*)readBuffer);
|
||||
// sanity check
|
||||
if (readSize > MAX_PACKETSIZE) {
|
||||
kill();
|
||||
return;
|
||||
}
|
||||
|
||||
// we'll just leave bufferIndex at 0 since we already have the packet size, it's safe to overwrite those bytes
|
||||
activelyReading = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (readSize > 0 && readBufferIndex < readSize) {
|
||||
// read until the end of the packet! (or at least try too)
|
||||
int recved = recv(sock, (buffer_t*)(readBuffer + readBufferIndex), readSize - readBufferIndex, 0);
|
||||
if (!SOCKETERROR(recved))
|
||||
readBufferIndex += recved;
|
||||
}
|
||||
|
||||
if (activelyReading && readBufferIndex - readSize <= 0) {
|
||||
// decrypt readBuffer and copy to CNPacketData
|
||||
CNSocketEncryption::decryptData(readBuffer, (uint8_t*)(&EKey), readSize);
|
||||
|
||||
// this doesn't leak memory because we free it in CNPacketData's deconstructor LOL
|
||||
void* tmpBuf = xmalloc(readSize-sizeof(int32_t));
|
||||
memcpy(tmpBuf, readBuffer+sizeof(uint32_t), readSize-sizeof(int32_t));
|
||||
CNPacketData tmp(tmpBuf, *((uint32_t*)readBuffer), readSize-sizeof(int32_t), EKey);
|
||||
|
||||
// CALL PACKET HANDLER!!
|
||||
pHandler(this, &tmp); // tmp's deconstructor will be called when readStep returns so that tmpBuffer we made will be cleaned up :)
|
||||
|
||||
// reset vars :)
|
||||
readSize = 0;
|
||||
readBufferIndex = 0;
|
||||
activelyReading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================[[ CNServer ]]========================================================
|
||||
|
||||
void CNServer::init() {
|
||||
// create socket file descriptor
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (SOCKETINVALID(sock)) {
|
||||
std::cerr << "[FATAL] OpenFusion: socket failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// attach socket to the port
|
||||
int opt = 1;
|
||||
#ifdef _WIN32
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)) != 0) {
|
||||
#else
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0) {
|
||||
#endif
|
||||
std::cerr << "[FATAL] OpenFusion: setsockopt failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
|
||||
addressSize = sizeof(address);
|
||||
|
||||
// Bind to the port
|
||||
if (SOCKETERROR(bind(sock, (struct sockaddr *)&address, addressSize))) {
|
||||
std::cerr << "[FATAL] OpenFusion: bind failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (SOCKETERROR(listen(sock, SOMAXCONN))) {
|
||||
std::cerr << "[FATAL] OpenFusion: listen failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// set server listener to non-blocking
|
||||
#ifdef _WIN32
|
||||
unsigned long mode = 1;
|
||||
if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
|
||||
#else
|
||||
if (fcntl(sock, F_SETFL, (fcntl(sock, F_GETFL, 0) | O_NONBLOCK)) != 0) {
|
||||
#endif
|
||||
std::cerr << "[FATAL] OpenFusion: fcntl failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
CNServer::CNServer() {};
|
||||
CNServer::CNServer(uint16_t p): port(p) {}
|
||||
|
||||
void CNServer::start() {
|
||||
std::cout << "Starting server at *:" << port << std::endl;
|
||||
// listen to new connections, add to connection list
|
||||
while (true) {
|
||||
// listen for a new connection
|
||||
SOCKET newConnection = accept(sock, (struct sockaddr *)&(address), (socklen_t*)&(addressSize));
|
||||
if (!SOCKETINVALID(newConnection)) {
|
||||
// new connection! make sure to set non-blocking!
|
||||
#ifdef _WIN32
|
||||
unsigned long mode = 1;
|
||||
if (ioctlsocket(newConnection, FIONBIO, &mode) != 0) {
|
||||
#else
|
||||
if (fcntl(newConnection, F_SETFL, (fcntl(sock, F_GETFL, 0) | O_NONBLOCK)) != 0) {
|
||||
#endif
|
||||
std::cerr << "[FATAL] OpenFusion: fcntl failed on new connection" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::cout << "New connection! " << inet_ntoa(address.sin_addr) << std::endl;
|
||||
|
||||
// add connection to list!
|
||||
CNSocket* tmp = new CNSocket(newConnection, pHandler);
|
||||
connections.push_back(tmp);
|
||||
}
|
||||
|
||||
// for each connection, check if it's alive, if not kill it!
|
||||
std::list<CNSocket*>::iterator i = connections.begin();
|
||||
while (i != connections.end()) {
|
||||
CNSocket* cSock = *i;
|
||||
|
||||
if (cSock->isAlive()) {
|
||||
cSock->step();
|
||||
|
||||
++i; // go to the next element
|
||||
} else {
|
||||
killConnection(cSock);
|
||||
connections.erase(i++);
|
||||
delete cSock;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
Sleep(0);
|
||||
#else
|
||||
sleep(0); // so your cpu isn't at 100% all the time, we don't need all of that! im not hacky! you're hacky!
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CNServer::killConnection(CNSocket* cns) {} // stubbed lol
|
138
src/CNProtocol.hpp
Normal file
138
src/CNProtocol.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#ifndef _CNP_HPP
|
||||
#define _CNP_HPP
|
||||
|
||||
#define MAX_PACKETSIZE 8192
|
||||
#define DEBUGLOG(x) x
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#ifdef _WIN32
|
||||
// windows (UNTESTED)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
typedef char buffer_t;
|
||||
//#define errno WSAGetLastError()
|
||||
#define SOCKETINVALID(x) (x == INVALID_SOCKET)
|
||||
#define SOCKETERROR(x) (x == SOCKET_ERROR)
|
||||
#else
|
||||
// posix platform
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef int SOCKET;
|
||||
typedef void buffer_t;
|
||||
#define SOCKETINVALID(x) (x < 0)
|
||||
#define SOCKETERROR(x) (x == -1)
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <csignal>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
|
||||
/*
|
||||
Packets format (sent from the client):
|
||||
[4 bytes] - size of packet (including these 4 bytes!)
|
||||
[size bytes] - Encrypted packet (byte swapped && xor'd with 8 byte key; see CNSocketEncryption)
|
||||
[4 bytes] - packet type (which is a combination of the first 4 bytes of the packet and a checksum in some versions)
|
||||
[structure]
|
||||
*/
|
||||
|
||||
// error checking calloc wrapper
|
||||
inline void* xmalloc(size_t sz) {
|
||||
void* res = calloc(1, sz);
|
||||
|
||||
if (res == NULL) {
|
||||
std::cerr << "[FATAL] OpenFusion: calloc failed to allocate memory!" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace CNSocketEncryption {
|
||||
// you won't believe how complicated they made it in the client :facepalm:
|
||||
static constexpr const char* defaultKey = "m@rQn~W#";
|
||||
static const unsigned int keyLength = 8;
|
||||
|
||||
int Encrypt_byte_change_A(int ERSize, uint8_t* data, int size);
|
||||
int xorData(uint8_t* buffer, uint8_t* key, int size);
|
||||
uint64_t createNewKey(uint64_t uTime, int32_t iv1, int32_t iv2);
|
||||
int encryptData(uint8_t* buffer, uint8_t* key, int size);
|
||||
int decryptData(uint8_t* buffer, uint8_t* key, int size);
|
||||
}
|
||||
|
||||
class CNPacketData {
|
||||
public:
|
||||
void* buf;
|
||||
int size;
|
||||
uint32_t type;
|
||||
uint64_t key;
|
||||
|
||||
CNPacketData(void* b, uint32_t t, int l, uint64_t k);
|
||||
~CNPacketData();
|
||||
};
|
||||
|
||||
class CNSocket;
|
||||
typedef void (*PacketHandler)(CNSocket* sock, CNPacketData* data);
|
||||
|
||||
class CNSocket {
|
||||
private:
|
||||
uint64_t EKey;
|
||||
uint64_t FEKey;
|
||||
int32_t readSize = 0;
|
||||
uint8_t* readBuffer = new uint8_t[MAX_PACKETSIZE];
|
||||
int readBufferIndex = 0;
|
||||
bool activelyReading = false;
|
||||
bool alive = true;
|
||||
|
||||
bool sendData(uint8_t* data, int size);
|
||||
|
||||
public:
|
||||
SOCKET sock;
|
||||
PacketHandler pHandler;
|
||||
|
||||
CNSocket(SOCKET s, PacketHandler ph);
|
||||
|
||||
void setEKey(uint64_t k);
|
||||
void setFEKey(uint64_t k);
|
||||
uint64_t getEKey();
|
||||
uint64_t getFEKey();
|
||||
|
||||
void kill();
|
||||
void sendPacket(CNPacketData* pak);
|
||||
void step();
|
||||
bool isAlive();
|
||||
};
|
||||
|
||||
// in charge of accepting new connections and making sure each connection is kept alive
|
||||
class CNServer {
|
||||
protected:
|
||||
std::list<CNSocket*> connections;
|
||||
|
||||
SOCKET sock;
|
||||
uint16_t port;
|
||||
socklen_t addressSize;
|
||||
struct sockaddr_in address;
|
||||
void init();
|
||||
|
||||
public:
|
||||
PacketHandler pHandler;
|
||||
|
||||
CNServer();
|
||||
CNServer(uint16_t p);
|
||||
|
||||
void start();
|
||||
virtual void killConnection(CNSocket* cns);
|
||||
};
|
||||
|
||||
#endif
|
33
src/CNShardServer.cpp
Normal file
33
src/CNShardServer.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "CNProtocol.hpp"
|
||||
#include "CNStructs.hpp"
|
||||
#include "CNShardServer.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
#include "CNShared.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
|
||||
std::map<uint32_t, PacketHandler> CNShardServer::ShardPackets;
|
||||
|
||||
CNShardServer::CNShardServer(uint16_t p) {
|
||||
port = p;
|
||||
pHandler = &CNShardServer::handlePacket;
|
||||
init();
|
||||
}
|
||||
|
||||
void CNShardServer::handlePacket(CNSocket* sock, CNPacketData* data) {
|
||||
if (ShardPackets.find(data->type) != ShardPackets.end())
|
||||
ShardPackets[data->type](sock, data);
|
||||
else
|
||||
std::cerr << "OpenFusion: SHARD UNIMPLM ERR. PacketType: " << data->type << std::endl;
|
||||
}
|
||||
|
||||
void CNShardServer::killConnection(CNSocket* cns) {
|
||||
// remove from CNSharedData
|
||||
Player cachedPlr = PlayerManager::getPlayer(cns);
|
||||
PlayerManager::removePlayer(cns);
|
||||
|
||||
CNSharedData::erasePlayer(cachedPlr.SerialKey);
|
||||
std::cout << U16toU8(cachedPlr.PCStyle.szFirstName) << " " << U16toU8(cachedPlr.PCStyle.szLastName) << " left" << std::endl;
|
||||
}
|
49
src/CNShardServer.hpp
Normal file
49
src/CNShardServer.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef _CNSS_HPP
|
||||
#define _CNSS_HPP
|
||||
|
||||
#include "CNProtocol.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
enum SHARDPACKETID {
|
||||
// client 2 shard
|
||||
P_CL2FE_REQ_PC_ENTER = 318767105,
|
||||
P_CL2FE_REQ_PC_LOADING_COMPLETE = 318767245,
|
||||
P_CL2FE_REQ_PC_MOVE = 318767107,
|
||||
P_CL2FE_REQ_PC_STOP = 318767108,
|
||||
P_CL2FE_REQ_PC_JUMP = 318767109,
|
||||
P_CL2FE_REQ_PC_MOVEPLATFORM = 318767168,
|
||||
P_CL2FE_REQ_PC_GOTO = 318767124,
|
||||
P_CL2FE_GM_REQ_PC_SET_VALUE = 318767211,
|
||||
P_CL2FE_REQ_SEND_FREECHAT_MESSAGE = 318767111,
|
||||
P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT = 318767184,
|
||||
|
||||
// shard 2 client
|
||||
P_FE2CL_REP_PC_ENTER_SUCC = 822083586,
|
||||
P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC = 822083833,
|
||||
P_FE2CL_PC_NEW = 822083587,
|
||||
P_FE2CL_PC_MOVE = 822083592,
|
||||
P_FE2CL_PC_STOP = 822083593,
|
||||
P_FE2CL_PC_JUMP = 822083594,
|
||||
P_FE2CL_PC_EXIT = 822083590,
|
||||
P_FE2CL_PC_MOVEPLATFORM = 822083704,
|
||||
P_FE2CL_REP_PC_GOTO_SUCC = 822083633,
|
||||
P_FE2CL_GM_REP_PC_SET_VALUE = 822083781,
|
||||
P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT = 822083730
|
||||
};
|
||||
|
||||
#define REGISTER_SHARD_PACKET(pactype, handlr) CNShardServer::ShardPackets[pactype] = handlr;
|
||||
|
||||
// WARNING: THERE CAN ONLY BE ONE OF THESE SERVERS AT A TIME!!!!!! TODO: change players & packet handlers to be non-static
|
||||
class CNShardServer : public CNServer {
|
||||
private:
|
||||
static void handlePacket(CNSocket* sock, CNPacketData* data);
|
||||
public:
|
||||
static std::map<uint32_t, PacketHandler> ShardPackets;
|
||||
|
||||
CNShardServer(uint16_t p);
|
||||
|
||||
void killConnection(CNSocket* cns);
|
||||
};
|
||||
|
||||
#endif
|
25
src/CNShared.cpp
Normal file
25
src/CNShared.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "CNShared.hpp"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include "mingw/mingw.mutex.h"
|
||||
#else
|
||||
#include <mutex>
|
||||
#endif
|
||||
std::map<int64_t, Player> CNSharedData::players;
|
||||
std::mutex playerCrit;
|
||||
|
||||
void CNSharedData::setPlayer(int64_t sk, Player& plr) {
|
||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
||||
|
||||
players[sk] = plr;
|
||||
}
|
||||
|
||||
Player CNSharedData::getPlayer(int64_t sk) {
|
||||
return players[sk];
|
||||
}
|
||||
|
||||
void CNSharedData::erasePlayer(int64_t sk) {
|
||||
std::lock_guard<std::mutex> lock(playerCrit); // the lock will be removed when the function ends
|
||||
|
||||
players.erase(sk);
|
||||
}
|
23
src/CNShared.hpp
Normal file
23
src/CNShared.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
CNShared.hpp
|
||||
There's some data shared between the Login Server and the Shard Server. Of course all of this needs to be thread-safe. No mucking about on this one!
|
||||
*/
|
||||
|
||||
#ifndef _CNSD_HPP
|
||||
#define _CNSD_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Player.hpp"
|
||||
|
||||
namespace CNSharedData {
|
||||
// serialkey corresponds to player data
|
||||
extern std::map<int64_t, Player> players;
|
||||
|
||||
void setPlayer(int64_t sk, Player& plr);
|
||||
Player getPlayer(int64_t sk);
|
||||
void erasePlayer(int64_t sk);
|
||||
}
|
||||
|
||||
#endif
|
24
src/CNStructs.cpp
Normal file
24
src/CNStructs.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "CNStructs.hpp"
|
||||
|
||||
std::string U16toU8(char16_t* src) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
|
||||
return convert.to_bytes(src);
|
||||
}
|
||||
|
||||
// returns number of char16_t that was written at des
|
||||
int U8toU16(std::string src, char16_t* des) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
|
||||
std::u16string tmp = convert.from_bytes(src);
|
||||
|
||||
// copy utf16 string to buffer
|
||||
memcpy(des, tmp.c_str(), sizeof(char16_t) * tmp.length());
|
||||
des[tmp.length()] = '\0';
|
||||
|
||||
return tmp.length();
|
||||
}
|
||||
|
||||
uint64_t getTime() {
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
return tp.tv_sec * 1000 + tp.tv_usec / 1000;
|
||||
}
|
533
src/CNStructs.hpp
Normal file
533
src/CNStructs.hpp
Normal file
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
CNStructs.hpp - defines some basic structs & useful methods for packets used by FusionFall
|
||||
|
||||
NOTE: this is missing the vast majority of packets, I have also ommitted the ERR & FAIL packets for simplicity
|
||||
*/
|
||||
|
||||
#ifndef _CNS_HPP
|
||||
#define _CNS_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
// TODO: rewrite U16toU8 & U8toU16 to not use codecvt
|
||||
|
||||
std::string U16toU8(char16_t* src);
|
||||
|
||||
// returns number of char16_t that was written at des
|
||||
int U8toU16(std::string src, char16_t* des);
|
||||
|
||||
uint64_t getTime();
|
||||
|
||||
//#define CNPROTO_VERSION_0728
|
||||
|
||||
#ifdef CNPROTO_VERSION_0728
|
||||
#define AEQUIP_COUNT 12
|
||||
#else
|
||||
#define AEQUIP_COUNT 9
|
||||
#endif
|
||||
|
||||
// ========================================================[[ General Purpose ]]========================================================
|
||||
|
||||
// sets the same byte alignment as the structs in the client
|
||||
#pragma pack(push, 4)
|
||||
struct sPCStyle {
|
||||
int64_t iPC_UID;
|
||||
int8_t iNameCheck;
|
||||
|
||||
char16_t szFirstName[9];
|
||||
char16_t szLastName[17];
|
||||
|
||||
int8_t iGender;
|
||||
int8_t iFaceStyle;
|
||||
int8_t iHairStyle;
|
||||
int8_t iHairColor;
|
||||
int8_t iSkinColor;
|
||||
int8_t iEyeColor;
|
||||
int8_t iHeight;
|
||||
int8_t iBody;
|
||||
int32_t iClass;
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct sPCStyle2 {
|
||||
int8_t iAppearanceFlag;
|
||||
int8_t iTutorialFlag;
|
||||
int8_t iPayzoneFlag;
|
||||
|
||||
sPCStyle2() {}
|
||||
sPCStyle2(int8_t a, int8_t t, int8_t p):
|
||||
iAppearanceFlag(a), iTutorialFlag(t), iPayzoneFlag(p) {}
|
||||
};
|
||||
|
||||
#pragma pack(2)
|
||||
struct sOnItem {
|
||||
int16_t iEquipHandID;
|
||||
int16_t iEquipUBID;
|
||||
int16_t iEquipLBID;
|
||||
int16_t iEquipFootID;
|
||||
int16_t iEquipHeadID;
|
||||
int16_t iEquipFaceID;
|
||||
int16_t iEquipBackID;
|
||||
};
|
||||
|
||||
struct sOnItem_Index {
|
||||
int16_t iEquipUBID_index;
|
||||
int16_t iEquipLBID_index;
|
||||
int16_t iEquipFootID_index;
|
||||
int16_t iFaceStyle;
|
||||
int16_t iHairStyle;
|
||||
};
|
||||
|
||||
struct sNano {
|
||||
int16_t iID;
|
||||
int16_t iSkillID;
|
||||
int16_t iStamina;
|
||||
};
|
||||
|
||||
#pragma pack(4)
|
||||
struct sItemBase {
|
||||
int16_t iType;
|
||||
int16_t iID;
|
||||
int32_t iOpt;
|
||||
int32_t iTimeLimit;
|
||||
|
||||
#ifdef CNPROTO_VERSION_0728
|
||||
int32_t iSerial;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct sTimeBuff {
|
||||
uint64_t iTimeLimit;
|
||||
uint64_t iTimeDuration;
|
||||
|
||||
int32_t iTimeRepeat;
|
||||
int32_t iValue;
|
||||
int32_t iConfirmNum;
|
||||
};
|
||||
|
||||
struct sRunningQuest {
|
||||
int32_t m_aCurrTaskID;
|
||||
|
||||
int32_t m_aKillNPCID[3];
|
||||
int32_t m_aKillNPCCount[3];
|
||||
int32_t m_aNeededItemID[3];
|
||||
int32_t m_aNeededItemCount[3];
|
||||
};
|
||||
|
||||
struct sPCLoadData2CL {
|
||||
int16_t iUserLevel;
|
||||
sPCStyle PCStyle;
|
||||
sPCStyle2 PCStyle2;
|
||||
|
||||
int16_t iLevel;
|
||||
int16_t iMentor;
|
||||
int16_t iMentorCount;
|
||||
int32_t iHP;
|
||||
int32_t iBatteryW;
|
||||
int32_t iBatteryN;
|
||||
int32_t iCandy;
|
||||
int32_t iFusionMatter;
|
||||
int8_t iSpecialState;
|
||||
int32_t iMapNum;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
int32_t iAngle;
|
||||
|
||||
sItemBase aEquip[AEQUIP_COUNT];
|
||||
sItemBase aInven[50];
|
||||
sItemBase aQInven[50];
|
||||
|
||||
sNano aNanoBank[37];
|
||||
|
||||
int16_t aNanoSlots[3];
|
||||
|
||||
int16_t iActiveNanoSlotNum;
|
||||
int32_t iConditionBitFlag;
|
||||
int32_t eCSTB___Add;
|
||||
|
||||
sTimeBuff TimeBuff;
|
||||
|
||||
int64_t aQuestFlag[32];
|
||||
int64_t aRepeatQuestFlag[8];
|
||||
|
||||
sRunningQuest aRunningQuest[9];
|
||||
|
||||
int32_t iCurrentMissionID;
|
||||
int32_t iWarpLocationFlag;
|
||||
|
||||
int64_t aWyvernLocationFlag[2];
|
||||
|
||||
int32_t iBuddyWarpTime;
|
||||
int32_t iFatigue;
|
||||
int32_t iFatigue_Level;
|
||||
int32_t iFatigueRate;
|
||||
int64_t iFirstUseFlag1;
|
||||
int64_t iFirstUseFlag2;
|
||||
|
||||
int32_t aiPCSkill[33];
|
||||
|
||||
sPCLoadData2CL() {};
|
||||
};
|
||||
|
||||
struct sPCAppearanceData {
|
||||
int32_t iID;
|
||||
sPCStyle PCStyle;
|
||||
|
||||
int32_t iConditionBitFlag;
|
||||
int8_t iPCState;
|
||||
int8_t iSpecialState;
|
||||
int16_t iLv;
|
||||
int32_t iHP;
|
||||
|
||||
int32_t iMapNum;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
int32_t iAngle;
|
||||
|
||||
sItemBase ItemEquip[AEQUIP_COUNT];
|
||||
sNano Nano;
|
||||
|
||||
int32_t eRT;
|
||||
};
|
||||
|
||||
// ========================================================[[ Client2LoginServer packets ]]========================================================
|
||||
|
||||
#pragma pack(4)
|
||||
struct sP_CL2LS_REQ_LOGIN {
|
||||
char16_t szID[33];
|
||||
char16_t szPassword[33];
|
||||
|
||||
int32_t iClientVerA;
|
||||
int32_t iClientVerB;
|
||||
int32_t iClientVerC;
|
||||
|
||||
int32_t iLoginType;
|
||||
|
||||
uint8_t szCookie_TEGid[64];
|
||||
uint8_t szCookie_authid[255];
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REQ_CHECK_CHAR_NAME {
|
||||
int32_t iFNCode;
|
||||
int32_t iLNCode;
|
||||
int32_t iMNCode;
|
||||
|
||||
char16_t szFirstName[9];
|
||||
char16_t szLastName[17];
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REQ_SAVE_CHAR_NAME {
|
||||
int8_t iSlotNum;
|
||||
int8_t iGender;
|
||||
int32_t iFNCode;
|
||||
int32_t iLNCode;
|
||||
int32_t iMNCode;
|
||||
|
||||
char16_t szFirstName[9];
|
||||
char16_t szLastName[17];
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REQ_CHAR_CREATE {
|
||||
sPCStyle PCStyle;
|
||||
sOnItem sOn_Item;
|
||||
sOnItem_Index sOn_Item_Index;
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REQ_CHAR_SELECT {
|
||||
int64_t iPC_UID;
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REP_LIVE_CHECK {
|
||||
int32_t unused;
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct sP_CL2LS_REQ_SHARD_SELECT {
|
||||
int8_t iShardNum;
|
||||
};
|
||||
|
||||
struct sP_CL2LS_REQ_SHARD_LIST_INFO {
|
||||
uint8_t unused;
|
||||
};
|
||||
|
||||
// ========================================================[[ LoginServer2Client packets ]]========================================================
|
||||
|
||||
#pragma pack(4)
|
||||
struct sP_LS2CL_REP_LOGIN_SUCC {
|
||||
int8_t iCharCount;
|
||||
int8_t iSlotNum;
|
||||
int8_t iPaymentFlag;
|
||||
int8_t iTempForPacking4; // UNUSED
|
||||
uint64_t uiSvrTime; // UNIX timestamp
|
||||
|
||||
char16_t szID[33];
|
||||
|
||||
uint32_t iOpenBetaFlag;
|
||||
};
|
||||
|
||||
#pragma pack(2)
|
||||
struct sP_LS2CL_REP_CHECK_CHAR_NAME_SUCC {
|
||||
char16_t szFirstName[9];
|
||||
char16_t szLastName[17];
|
||||
};
|
||||
|
||||
#pragma pack(4)
|
||||
struct sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC {
|
||||
int64_t iPC_UID;
|
||||
int8_t iSlotNum;
|
||||
int8_t iGender;
|
||||
|
||||
char16_t szFirstName[9];
|
||||
char16_t szLastName[17];
|
||||
};
|
||||
|
||||
struct sP_LS2CL_REP_CHAR_CREATE_SUCC {
|
||||
int16_t iLevel;
|
||||
sPCStyle PC_Style;
|
||||
sPCStyle2 PC_Style2;
|
||||
sOnItem sOn_Item;
|
||||
};
|
||||
|
||||
struct sP_LS2CL_REP_CHAR_INFO {
|
||||
int8_t iSlot;
|
||||
int16_t iLevel;
|
||||
|
||||
sPCStyle sPC_Style;
|
||||
sPCStyle2 sPC_Style2;
|
||||
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
|
||||
sItemBase aEquip[AEQUIP_COUNT];
|
||||
};
|
||||
|
||||
struct sP_LS2CL_REP_SHARD_SELECT_SUCC {
|
||||
uint8_t g_FE_ServerIP[16]; // Ascii
|
||||
int32_t g_FE_ServerPort;
|
||||
int64_t iEnterSerialKey;
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct sP_LS2CL_REP_CHAR_SELECT_SUCC {
|
||||
int8_t unused;
|
||||
};
|
||||
|
||||
struct sP_LS2CL_REP_SHARD_LIST_INFO_SUCC {
|
||||
uint8_t aShardConnectFlag[27];
|
||||
};
|
||||
|
||||
|
||||
// ========================================================[[ Client2ShardServer packets ]]========================================================
|
||||
|
||||
#pragma pack(4)
|
||||
struct sP_CL2FE_REQ_PC_ENTER {
|
||||
char16_t szID[33];
|
||||
int32_t iTempValue;
|
||||
int64_t iEnterSerialKey;
|
||||
};
|
||||
|
||||
|
||||
struct sP_CL2FE_REQ_PC_LOADING_COMPLETE {
|
||||
int32_t iPC_ID;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_MOVE {
|
||||
uint64_t iCliTime;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
float fVX;
|
||||
float fVY;
|
||||
float fVZ;
|
||||
int32_t iAngle;
|
||||
uint8_t cKeyValue;
|
||||
int32_t iSpeed;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_STOP {
|
||||
uint64_t iCliTime;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_JUMP {
|
||||
uint64_t iCliTime;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
int32_t iVX;
|
||||
int32_t iVY;
|
||||
int32_t iVZ;
|
||||
int32_t iAngle;
|
||||
uint8_t cKeyValue;
|
||||
int32_t iSpeed;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_MOVEPLATFORM {
|
||||
uint64_t iCliTime;
|
||||
|
||||
int32_t iLcX;
|
||||
int32_t iLcY;
|
||||
int32_t iLcZ;
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
|
||||
float fVX;
|
||||
float fVY;
|
||||
float fVZ;
|
||||
|
||||
int32_t bDown;
|
||||
uint32_t iPlatformID;
|
||||
int32_t iAngle;
|
||||
uint32_t cKeyValue;
|
||||
int32_t iSpeed;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_GOTO {
|
||||
int32_t iToX;
|
||||
int32_t iToY;
|
||||
int32_t iToZ;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_GM_REQ_PC_SET_VALUE {
|
||||
int32_t iPC_ID;
|
||||
int32_t iSetValueType;
|
||||
int32_t iSetValue;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE {
|
||||
char16_t szFreeChat[128];
|
||||
int32_t iEmoteCode;
|
||||
};
|
||||
|
||||
struct sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT {
|
||||
int32_t iID_From;
|
||||
int32_t iEmoteCode;
|
||||
};
|
||||
|
||||
// ========================================================[[ ShardServer2Client packets ]]========================================================
|
||||
|
||||
struct sP_FE2CL_REP_PC_ENTER_SUCC {
|
||||
int32_t iID;
|
||||
sPCLoadData2CL PCLoadData2CL;
|
||||
uint64_t uiSvrTime;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC {
|
||||
int32_t iPC_ID;
|
||||
};
|
||||
|
||||
// literally just a wrapper for a sPCAppearanceData struct :/
|
||||
struct sP_FE2CL_PC_NEW {
|
||||
sPCAppearanceData PCAppearanceData;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_PC_MOVE {
|
||||
uint64_t iCliTime;
|
||||
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
float fVX;
|
||||
float fVY;
|
||||
float fVZ;
|
||||
int32_t iAngle;
|
||||
uint8_t cKeyValue;
|
||||
|
||||
int32_t iSpeed;
|
||||
|
||||
int32_t iID;
|
||||
|
||||
uint64_t iSvrTime;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_PC_STOP {
|
||||
uint64_t iCliTime;
|
||||
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
int32_t iID;
|
||||
|
||||
uint64_t iSvrTime;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_PC_JUMP {
|
||||
uint64_t iCliTime;
|
||||
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
int32_t iVX;
|
||||
int32_t iVY;
|
||||
int32_t iVZ;
|
||||
int32_t iAngle;
|
||||
uint8_t cKeyValue;
|
||||
int32_t iSpeed;
|
||||
int32_t iID;
|
||||
|
||||
uint64_t iSvrTime;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_PC_MOVEPLATFORM {
|
||||
uint64_t iCliTime;
|
||||
|
||||
int32_t iLcX;
|
||||
int32_t iLcY;
|
||||
int32_t iLcZ;
|
||||
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
|
||||
float fVX;
|
||||
float fVY;
|
||||
float fVZ;
|
||||
|
||||
int32_t bDown;
|
||||
uint32_t iPlatformID;
|
||||
int32_t iAngle;
|
||||
int8_t cKeyValue;
|
||||
int32_t iSpeed;
|
||||
int32_t iPC_ID;
|
||||
uint64_t iSvrTime;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_GM_REP_PC_SET_VALUE {
|
||||
int32_t iPC_ID;
|
||||
int32_t iSetValueType;
|
||||
int32_t iSetValue;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_PC_EXIT {
|
||||
int32_t iID;
|
||||
int32_t iExitType;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_REP_PC_GOTO_SUCC {
|
||||
int32_t iX;
|
||||
int32_t iY;
|
||||
int32_t iZ;
|
||||
};
|
||||
|
||||
struct sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT {
|
||||
int32_t iID_From;
|
||||
int32_t iEmoteCode;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#endif
|
37
src/ChatManager.cpp
Normal file
37
src/ChatManager.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "CNShardServer.hpp"
|
||||
#include "CNStructs.hpp"
|
||||
#include "ChatManager.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
|
||||
void ChatManager::init() {
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SEND_FREECHAT_MESSAGE, chatHandler);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT, emoteHandler);
|
||||
}
|
||||
|
||||
void ChatManager::chatHandler(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_FREECHAT_MESSAGE*)data->buf;
|
||||
|
||||
// stubbed for now
|
||||
}
|
||||
|
||||
void ChatManager::emoteHandler(CNSocket* sock, CNPacketData* data) {
|
||||
// you can dance with friends!!!!!!!!
|
||||
|
||||
sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT* emote = (sP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT*)data->buf;
|
||||
|
||||
PlayerView plr = PlayerManager::players[sock];
|
||||
|
||||
// send to client
|
||||
sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT* resp = (sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT*)xmalloc(sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
|
||||
resp->iEmoteCode = emote->iEmoteCode;
|
||||
resp->iID_From = plr.plr.iID;
|
||||
sock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT), sock->getFEKey()));
|
||||
|
||||
// send to visible players (players within render distance)
|
||||
for (CNSocket* otherSock : plr.viewable) {
|
||||
resp = (sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT*)xmalloc(sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT));
|
||||
resp->iEmoteCode = emote->iEmoteCode;
|
||||
resp->iID_From = plr.plr.iID;
|
||||
otherSock->sendPacket(new CNPacketData((void*)resp, P_FE2CL_REP_PC_AVATAR_EMOTES_CHAT, sizeof(sP_FE2CL_REP_PC_AVATAR_EMOTES_CHAT), otherSock->getFEKey()));
|
||||
}
|
||||
}
|
13
src/ChatManager.hpp
Normal file
13
src/ChatManager.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef _CM_HPP
|
||||
#define _CM_HPP
|
||||
|
||||
#include "CNShardServer.hpp"
|
||||
|
||||
namespace ChatManager {
|
||||
void init();
|
||||
|
||||
void chatHandler(CNSocket* sock, CNPacketData* data);
|
||||
void emoteHandler(CNSocket* sock, CNPacketData* data);
|
||||
}
|
||||
|
||||
#endif
|
462
src/INIReader.hpp
Normal file
462
src/INIReader.hpp
Normal file
@@ -0,0 +1,462 @@
|
||||
// Read an INI file into easy-to-access name/value pairs.
|
||||
|
||||
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
|
||||
// Go to the project home page for more info:
|
||||
//
|
||||
// https://github.com/benhoyt/inih
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __INI_H__
|
||||
#define __INI_H__
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file. */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
inline static char* rstrip(char* s)
|
||||
{
|
||||
char* p = s + strlen(s);
|
||||
while (p > s && isspace((unsigned char)(*--p)))
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
inline static char* lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to null at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
inline static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||
inline static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
strncpy(dest, src, size);
|
||||
dest[size - 1] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
#else
|
||||
char* line;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)malloc(INI_MAX_LINE);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, INI_MAX_LINE, stream) != NULL) {
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (*start == ';' || *start == '#') {
|
||||
/* Per Python configparser, allow both ; and # comments at the
|
||||
start of a line */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(start, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
rstrip(start);
|
||||
#endif
|
||||
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!handler(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = lskip(end + 1);
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!handler(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
inline int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif /* __INI_H__ */
|
||||
|
||||
|
||||
#ifndef __INIREADER_H__
|
||||
#define __INIREADER_H__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
|
||||
// for simplicity here rather than speed, but it should be pretty decent.)
|
||||
class INIReader
|
||||
{
|
||||
public:
|
||||
// Empty Constructor
|
||||
INIReader() {};
|
||||
|
||||
// Construct INIReader and parse given filename. See ini.h for more info
|
||||
// about the parsing.
|
||||
INIReader(std::string filename);
|
||||
|
||||
// Construct INIReader and parse given file. See ini.h for more info
|
||||
// about the parsing.
|
||||
INIReader(FILE *file);
|
||||
|
||||
// Return the result of ini_parse(), i.e., 0 on success, line number of
|
||||
// first error on parse error, or -1 on file open error.
|
||||
int ParseError() const;
|
||||
|
||||
// Return the list of sections found in ini file
|
||||
const std::set<std::string>& Sections() const;
|
||||
|
||||
// Get a string value from INI file, returning default_value if not found.
|
||||
std::string Get(std::string section, std::string name,
|
||||
std::string default_value) const;
|
||||
|
||||
// Get an integer (long) value from INI file, returning default_value if
|
||||
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
|
||||
long GetInteger(std::string section, std::string name, long default_value) const;
|
||||
|
||||
// Get a real (floating point double) value from INI file, returning
|
||||
// default_value if not found or not a valid floating point value
|
||||
// according to strtod().
|
||||
double GetReal(std::string section, std::string name, double default_value) const;
|
||||
|
||||
// Get a single precision floating point number value from INI file, returning
|
||||
// default_value if not found or not a valid floating point value
|
||||
// according to strtof().
|
||||
float GetFloat(std::string section, std::string name, float default_value) const;
|
||||
|
||||
// Get a boolean value from INI file, returning default_value if not found or if
|
||||
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
|
||||
// and valid false values are "false", "no", "off", "0" (not case sensitive).
|
||||
bool GetBoolean(std::string section, std::string name, bool default_value) const;
|
||||
|
||||
protected:
|
||||
int _error;
|
||||
std::map<std::string, std::string> _values;
|
||||
std::set<std::string> _sections;
|
||||
static std::string MakeKey(std::string section, std::string name);
|
||||
static int ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value);
|
||||
};
|
||||
|
||||
#endif // __INIREADER_H__
|
||||
|
||||
|
||||
#ifndef __INIREADER__
|
||||
#define __INIREADER__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
|
||||
inline INIReader::INIReader(std::string filename)
|
||||
{
|
||||
_error = ini_parse(filename.c_str(), ValueHandler, this);
|
||||
}
|
||||
|
||||
inline INIReader::INIReader(FILE *file)
|
||||
{
|
||||
_error = ini_parse_file(file, ValueHandler, this);
|
||||
}
|
||||
|
||||
inline int INIReader::ParseError() const
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
inline const std::set<std::string>& INIReader::Sections() const
|
||||
{
|
||||
return _sections;
|
||||
}
|
||||
|
||||
inline std::string INIReader::Get(std::string section, std::string name, std::string default_value) const
|
||||
{
|
||||
std::string key = MakeKey(section, name);
|
||||
return _values.count(key) ? _values.at(key) : default_value;
|
||||
}
|
||||
|
||||
inline long INIReader::GetInteger(std::string section, std::string name, long default_value) const
|
||||
{
|
||||
std::string valstr = Get(section, name, "");
|
||||
const char* value = valstr.c_str();
|
||||
char* end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
long n = strtol(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
inline double INIReader::GetReal(std::string section, std::string name, double default_value) const
|
||||
{
|
||||
std::string valstr = Get(section, name, "");
|
||||
const char* value = valstr.c_str();
|
||||
char* end;
|
||||
double n = strtod(value, &end);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
inline float INIReader::GetFloat(std::string section, std::string name, float default_value) const
|
||||
{
|
||||
std::string valstr = Get(section, name, "");
|
||||
const char* value = valstr.c_str();
|
||||
char* end;
|
||||
float n = strtof(value, &end);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
inline bool INIReader::GetBoolean(std::string section, std::string name, bool default_value) const
|
||||
{
|
||||
std::string valstr = Get(section, name, "");
|
||||
// Convert to lower case to make string comparisons case-insensitive
|
||||
std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
|
||||
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
|
||||
return true;
|
||||
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
|
||||
return false;
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
inline std::string INIReader::MakeKey(std::string section, std::string name)
|
||||
{
|
||||
std::string key = section + "=" + name;
|
||||
// Convert to lower case to make section/name lookups case-insensitive
|
||||
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
||||
return key;
|
||||
}
|
||||
|
||||
inline int INIReader::ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value)
|
||||
{
|
||||
INIReader* reader = (INIReader*)user;
|
||||
std::string key = MakeKey(section, name);
|
||||
if (reader->_values[key].size() > 0)
|
||||
reader->_values[key] += "\n";
|
||||
reader->_values[key] += value;
|
||||
reader->_sections.insert(section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // __INIREADER__
|
1
src/Player.cpp
Normal file
1
src/Player.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "Player.hpp"
|
25
src/Player.hpp
Normal file
25
src/Player.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _PLR_HPP
|
||||
#define _PLR_HPP
|
||||
|
||||
#include "CNProtocol.hpp"
|
||||
#include "CNStructs.hpp"
|
||||
|
||||
struct Player {
|
||||
int64_t SerialKey;
|
||||
int32_t iID;
|
||||
uint64_t FEKey;
|
||||
|
||||
int level;
|
||||
int HP;
|
||||
int slot;
|
||||
sPCStyle PCStyle;
|
||||
sPCStyle2 PCStyle2;
|
||||
|
||||
int x, y, z, angle;
|
||||
sItemBase Equip[AEQUIP_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
364
src/PlayerManager.cpp
Normal file
364
src/PlayerManager.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "CNProtocol.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
#include "CNShardServer.hpp"
|
||||
#include "CNShared.hpp"
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
std::map<CNSocket*, PlayerView> PlayerManager::players;
|
||||
|
||||
void PlayerManager::init() {
|
||||
// register packet types
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ENTER, PlayerManager::enterPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_LOADING_COMPLETE, PlayerManager::loadPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_MOVE, PlayerManager::movePlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_STOP, PlayerManager::stopPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_JUMP, PlayerManager::jumpPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_MOVEPLATFORM, PlayerManager::movePlatformPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_GOTO, PlayerManager::gotoPlayer);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_GM_REQ_PC_SET_VALUE, PlayerManager::setSpecialPlayer);
|
||||
}
|
||||
|
||||
void PlayerManager::addPlayer(CNSocket* key, Player plr) {
|
||||
players[key] = PlayerView();
|
||||
players[key].viewable = std::list<CNSocket*>();
|
||||
players[key].plr = plr;
|
||||
}
|
||||
|
||||
void PlayerManager::removePlayer(CNSocket* key) {
|
||||
PlayerView cachedView = players[key];
|
||||
|
||||
// if players have them in their viewable lists, remove it
|
||||
for (CNSocket* otherSock : players[key].viewable) {
|
||||
players[otherSock].viewable.remove(key); // gone
|
||||
|
||||
// now sent PC_EXIT packet
|
||||
sP_FE2CL_PC_EXIT* exitPacket = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
|
||||
exitPacket->iID = players[key].plr.iID;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), otherSock->getFEKey()));
|
||||
}
|
||||
|
||||
players.erase(key);
|
||||
}
|
||||
|
||||
Player PlayerManager::getPlayer(CNSocket* key) {
|
||||
return players[key].plr;
|
||||
}
|
||||
|
||||
void PlayerManager::updatePlayerPosition(CNSocket* sock, int X, int Y, int Z) {
|
||||
players[sock].plr.x = X;
|
||||
players[sock].plr.y = Y;
|
||||
players[sock].plr.z = Z;
|
||||
|
||||
std::vector<CNSocket*> noView;
|
||||
std::vector<CNSocket*> yesView;
|
||||
|
||||
// TODO: oh god this is sooooo perfomance heavy the more players you have
|
||||
for (auto pair : players) {
|
||||
if (pair.first == sock)
|
||||
continue; // ignore our own connection
|
||||
|
||||
int diffX = abs(pair.second.plr.x - X); // the map is like a grid, X and Y are your position on the map, Z is the height. very different from other games...
|
||||
int diffY = abs(pair.second.plr.y - Y);
|
||||
|
||||
double dist = sqrt(pow(diffX, 2) + pow(diffY, 2));
|
||||
|
||||
if (dist > settings::VIEWDISTANCE) {
|
||||
noView.push_back(pair.first);
|
||||
} else {
|
||||
yesView.push_back(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<CNSocket*> cachedview(players[sock].viewable); // copies the viewable
|
||||
|
||||
for (CNSocket* otherSock : cachedview) {
|
||||
if (std::find(noView.begin(), noView.end(), otherSock) != noView.end()) {
|
||||
// sock shouldn't be visible, send PC_EXIT packet & remove them
|
||||
|
||||
sP_FE2CL_PC_EXIT* exitPacket = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
|
||||
sP_FE2CL_PC_EXIT* exitPacketOther = (sP_FE2CL_PC_EXIT*)xmalloc(sizeof(sP_FE2CL_PC_EXIT));
|
||||
|
||||
exitPacket->iID = players[sock].plr.iID;
|
||||
exitPacketOther->iID = players[otherSock].plr.iID;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)exitPacket, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), otherSock->getFEKey()));
|
||||
sock->sendPacket(new CNPacketData((void*)exitPacketOther, P_FE2CL_PC_EXIT, sizeof(sP_FE2CL_PC_EXIT), sock->getFEKey()));
|
||||
|
||||
players[sock].viewable.remove(otherSock);
|
||||
players[otherSock].viewable.remove(sock);
|
||||
}
|
||||
}
|
||||
|
||||
cachedview = players[sock].viewable;
|
||||
|
||||
for (CNSocket* otherSock : yesView) {
|
||||
if (std::find(cachedview.begin(), cachedview.end(), otherSock) == cachedview.end()) {
|
||||
// this needs to be added to the viewable players, send PC_ENTER
|
||||
|
||||
sP_FE2CL_PC_NEW* newPlayer = (sP_FE2CL_PC_NEW*)xmalloc(sizeof(sP_FE2CL_PC_NEW)); // current connection to other player
|
||||
sP_FE2CL_PC_NEW* newOtherPlayer = (sP_FE2CL_PC_NEW*)xmalloc(sizeof(sP_FE2CL_PC_NEW)); // other player to current connection
|
||||
|
||||
Player otherPlr = players[otherSock].plr;
|
||||
Player plr = players[sock].plr;
|
||||
|
||||
newPlayer->PCAppearanceData.iID = plr.iID;
|
||||
newPlayer->PCAppearanceData.iHP = plr.HP;
|
||||
newPlayer->PCAppearanceData.iLv = plr.level;
|
||||
newPlayer->PCAppearanceData.iX = plr.x;
|
||||
newPlayer->PCAppearanceData.iY = plr.y;
|
||||
newPlayer->PCAppearanceData.iZ = plr.z;
|
||||
newPlayer->PCAppearanceData.iAngle = plr.angle;
|
||||
newPlayer->PCAppearanceData.PCStyle = plr.PCStyle;
|
||||
memcpy(newPlayer->PCAppearanceData.ItemEquip, plr.Equip, sizeof(sItemBase) * AEQUIP_COUNT);
|
||||
|
||||
newOtherPlayer->PCAppearanceData.iID = otherPlr.iID;
|
||||
newOtherPlayer->PCAppearanceData.iHP = otherPlr.HP;
|
||||
newOtherPlayer->PCAppearanceData.iLv = otherPlr.level;
|
||||
newOtherPlayer->PCAppearanceData.iX = otherPlr.x;
|
||||
newOtherPlayer->PCAppearanceData.iY = otherPlr.y;
|
||||
newOtherPlayer->PCAppearanceData.iZ = otherPlr.z;
|
||||
newOtherPlayer->PCAppearanceData.iAngle = otherPlr.angle;
|
||||
newOtherPlayer->PCAppearanceData.PCStyle = otherPlr.PCStyle;
|
||||
memcpy(newOtherPlayer->PCAppearanceData.ItemEquip, otherPlr.Equip, sizeof(sItemBase) * AEQUIP_COUNT);
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)newOtherPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW), sock->getFEKey()));
|
||||
otherSock->sendPacket(new CNPacketData((void*)newPlayer, P_FE2CL_PC_NEW, sizeof(sP_FE2CL_PC_NEW), otherSock->getFEKey()));
|
||||
|
||||
players[sock].viewable.push_back(otherSock);
|
||||
players[otherSock].viewable.push_back(sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::enterPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_ENTER* enter = (sP_CL2FE_REQ_PC_ENTER*)data->buf;
|
||||
sP_FE2CL_REP_PC_ENTER_SUCC* response = (sP_FE2CL_REP_PC_ENTER_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_ENTER_SUCC));
|
||||
|
||||
// TODO: check if serialkey exists, if it doesn't send sP_FE2CL_REP_PC_ENTER_FAIL
|
||||
Player plr = CNSharedData::getPlayer(enter->iEnterSerialKey);
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_REQ_PC_ENTER:" << std::endl;
|
||||
std::cout << "\tID: " << U16toU8(enter->szID) << std::endl;
|
||||
std::cout << "\tSerial: " << enter->iEnterSerialKey << std::endl;
|
||||
std::cout << "\tTemp: " << enter->iTempValue << std::endl;
|
||||
std::cout << "\tPC_UID: " << plr.PCStyle.iPC_UID << std::endl;
|
||||
)
|
||||
|
||||
response->iID = rand();
|
||||
response->uiSvrTime = getTime();
|
||||
response->PCLoadData2CL.iUserLevel = 1;
|
||||
response->PCLoadData2CL.iHP = 1000 * plr.level;
|
||||
response->PCLoadData2CL.iLevel = plr.level;
|
||||
response->PCLoadData2CL.iMentor = 1;
|
||||
response->PCLoadData2CL.iMentorCount = 4;
|
||||
response->PCLoadData2CL.iMapNum = 0;
|
||||
response->PCLoadData2CL.iX = plr.x;
|
||||
response->PCLoadData2CL.iY = plr.y;
|
||||
response->PCLoadData2CL.iZ = plr.z;
|
||||
response->PCLoadData2CL.iActiveNanoSlotNum = -1;
|
||||
response->PCLoadData2CL.iFatigue = 50;
|
||||
response->PCLoadData2CL.PCStyle = plr.PCStyle;
|
||||
response->PCLoadData2CL.PCStyle2 = plr.PCStyle2;
|
||||
|
||||
for (int i = 0; i < AEQUIP_COUNT; i++)
|
||||
response->PCLoadData2CL.aEquip[i] = plr.Equip[i];
|
||||
|
||||
// don't ask..
|
||||
for (int i = 1; i < 37; i++) {
|
||||
response->PCLoadData2CL.aNanoBank[i].iID = i;
|
||||
response->PCLoadData2CL.aNanoBank[i].iSkillID = 1;
|
||||
response->PCLoadData2CL.aNanoBank[i].iStamina = 150;
|
||||
}
|
||||
|
||||
response->PCLoadData2CL.aNanoSlots[0] = 1;
|
||||
response->PCLoadData2CL.aNanoSlots[1] = 2;
|
||||
response->PCLoadData2CL.aNanoSlots[2] = 3;
|
||||
|
||||
response->PCLoadData2CL.aQuestFlag[0] = -1;
|
||||
|
||||
plr.iID = response->iID;
|
||||
plr.SerialKey = enter->iEnterSerialKey;
|
||||
plr.HP = response->PCLoadData2CL.iHP;
|
||||
|
||||
sock->setEKey(CNSocketEncryption::createNewKey(response->uiSvrTime, response->iID + 1, response->PCLoadData2CL.iFusionMatter + 1));
|
||||
sock->setFEKey(plr.FEKey);
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_ENTER_SUCC, sizeof(sP_FE2CL_REP_PC_ENTER_SUCC), sock->getFEKey()));
|
||||
|
||||
addPlayer(sock, plr);
|
||||
}
|
||||
|
||||
void PlayerManager::loadPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_LOADING_COMPLETE* complete = (sP_CL2FE_REQ_PC_LOADING_COMPLETE*)data->buf;
|
||||
sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC* response = (sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_REQ_PC_LOADING_COMPLETE:" << std::endl;
|
||||
std::cout << "\tPC_ID: " << complete->iPC_ID << std::endl;
|
||||
)
|
||||
|
||||
response->iPC_ID = complete->iPC_ID;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_LOADING_COMPLETE_SUCC, sizeof(sP_FE2CL_REP_PC_LOADING_COMPLETE_SUCC), sock->getFEKey()));
|
||||
}
|
||||
|
||||
void PlayerManager::movePlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_MOVE* moveData = (sP_CL2FE_REQ_PC_MOVE*)data->buf;
|
||||
updatePlayerPosition(sock, moveData->iX, moveData->iY, moveData->iZ);
|
||||
|
||||
players[sock].plr.angle = moveData->iAngle;
|
||||
uint64_t tm = getTime();
|
||||
|
||||
for (CNSocket* otherSock : players[sock].viewable) {
|
||||
sP_FE2CL_PC_MOVE* moveResponse = (sP_FE2CL_PC_MOVE*)xmalloc(sizeof(sP_FE2CL_PC_MOVE));
|
||||
|
||||
moveResponse->iID = players[sock].plr.iID;
|
||||
moveResponse->cKeyValue = moveData->cKeyValue;
|
||||
|
||||
moveResponse->iX = moveData->iX;
|
||||
moveResponse->iY = moveData->iY;
|
||||
moveResponse->iZ = moveData->iZ;
|
||||
moveResponse->iAngle = moveData->iAngle;
|
||||
moveResponse->fVX = moveData->fVX;
|
||||
moveResponse->fVY = moveData->fVY;
|
||||
moveResponse->fVZ = moveData->fVZ;
|
||||
|
||||
moveResponse->iSpeed = moveData->iSpeed;
|
||||
moveResponse->iCliTime = moveData->iCliTime; // maybe don't send this??? seems unneeded...
|
||||
moveResponse->iSvrTime = tm;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)moveResponse, P_FE2CL_PC_MOVE, sizeof(sP_FE2CL_PC_MOVE), otherSock->getFEKey()));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::stopPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_STOP* stopData = (sP_CL2FE_REQ_PC_STOP*)data->buf;
|
||||
updatePlayerPosition(sock, stopData->iX, stopData->iY, stopData->iZ);
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_REQ_PC_STOP:" << std::endl;
|
||||
std::cout << "\tX: " << stopData->iX << std::endl;
|
||||
std::cout << "\tY: " << stopData->iY << std::endl;
|
||||
std::cout << "\tZ: " << stopData->iZ << std::endl;
|
||||
)
|
||||
|
||||
uint64_t tm = getTime();
|
||||
|
||||
for (CNSocket* otherSock : players[sock].viewable) {
|
||||
sP_FE2CL_PC_STOP* stopResponse = (sP_FE2CL_PC_STOP*)xmalloc(sizeof(sP_FE2CL_PC_STOP));
|
||||
|
||||
stopResponse->iID = players[sock].plr.iID;
|
||||
|
||||
stopResponse->iX = stopData->iX;
|
||||
stopResponse->iY = stopData->iY;
|
||||
stopResponse->iZ = stopData->iZ;
|
||||
|
||||
stopResponse->iCliTime = stopData->iCliTime; // maybe don't send this??? seems unneeded...
|
||||
stopResponse->iSvrTime = tm;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)stopResponse, P_FE2CL_PC_STOP, sizeof(sP_FE2CL_PC_STOP), otherSock->getFEKey()));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::jumpPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_JUMP* jumpData = (sP_CL2FE_REQ_PC_JUMP*)data->buf;
|
||||
updatePlayerPosition(sock, jumpData->iX, jumpData->iY, jumpData->iZ);
|
||||
|
||||
uint64_t tm = getTime();
|
||||
|
||||
for (CNSocket* otherSock : players[sock].viewable) {
|
||||
sP_FE2CL_PC_JUMP* jumpResponse = (sP_FE2CL_PC_JUMP*)xmalloc(sizeof(sP_FE2CL_PC_JUMP));
|
||||
|
||||
jumpResponse->iID = players[sock].plr.iID;
|
||||
jumpResponse->cKeyValue = jumpData->cKeyValue;
|
||||
|
||||
jumpResponse->iX = jumpData->iX;
|
||||
jumpResponse->iY = jumpData->iY;
|
||||
jumpResponse->iZ = jumpData->iZ;
|
||||
jumpResponse->iAngle = jumpData->iAngle;
|
||||
jumpResponse->iVX = jumpData->iVX;
|
||||
jumpResponse->iVY = jumpData->iVY;
|
||||
jumpResponse->iVZ = jumpData->iVZ;
|
||||
|
||||
jumpResponse->iSpeed = jumpData->iSpeed;
|
||||
jumpResponse->iCliTime = jumpData->iCliTime; // maybe don't send this??? seems unneeded...
|
||||
jumpResponse->iSvrTime = tm;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)jumpResponse, P_FE2CL_PC_JUMP, sizeof(sP_FE2CL_PC_JUMP), otherSock->getFEKey()));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::movePlatformPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_MOVEPLATFORM* platformData = (sP_CL2FE_REQ_PC_MOVEPLATFORM*)data->buf;
|
||||
updatePlayerPosition(sock, platformData->iX, platformData->iY, platformData->iZ);
|
||||
|
||||
uint64_t tm = getTime();
|
||||
|
||||
for (CNSocket* otherSock : players[sock].viewable) {
|
||||
|
||||
sP_FE2CL_PC_MOVEPLATFORM* platResponse = (sP_FE2CL_PC_MOVEPLATFORM*)xmalloc(sizeof(sP_FE2CL_PC_MOVEPLATFORM));
|
||||
|
||||
platResponse->iPC_ID = players[sock].plr.iID;
|
||||
platResponse->iCliTime = platformData->iCliTime;
|
||||
platResponse->iSvrTime = tm;
|
||||
platResponse->iX = platformData->iX;
|
||||
platResponse->iY = platformData->iY;
|
||||
platResponse->iZ = platformData->iZ;
|
||||
platResponse->iAngle = platformData->iAngle;
|
||||
platResponse->fVX = platformData->fVX;
|
||||
platResponse->fVY = platformData->fVY;
|
||||
platResponse->fVZ = platformData->fVZ;
|
||||
platResponse->iLcX = platformData->iLcX;
|
||||
platResponse->iLcY = platformData->iLcY;
|
||||
platResponse->iLcZ = platformData->iLcZ;
|
||||
platResponse->iSpeed = platformData->iSpeed;
|
||||
platResponse->bDown = platformData->bDown;
|
||||
platResponse->cKeyValue = platformData->cKeyValue;
|
||||
platResponse->iPlatformID = platformData->iPlatformID;
|
||||
|
||||
otherSock->sendPacket(new CNPacketData((void*)platResponse, P_FE2CL_PC_MOVEPLATFORM, sizeof(sP_FE2CL_PC_MOVEPLATFORM), otherSock->getFEKey()));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_REQ_PC_GOTO* gotoData = (sP_CL2FE_REQ_PC_GOTO*)data->buf;
|
||||
sP_FE2CL_REP_PC_GOTO_SUCC* response = (sP_FE2CL_REP_PC_GOTO_SUCC*)xmalloc(sizeof(sP_FE2CL_REP_PC_GOTO_SUCC));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_REQ_PC_GOTO:" << std::endl;
|
||||
std::cout << "\tX: " << gotoData->iToX << std::endl;
|
||||
std::cout << "\tY: " << gotoData->iToY << std::endl;
|
||||
std::cout << "\tZ: " << gotoData->iToZ << std::endl;
|
||||
)
|
||||
|
||||
response->iX = gotoData->iToX;
|
||||
response->iY = gotoData->iToY;
|
||||
response->iZ = gotoData->iToZ;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_REP_PC_GOTO_SUCC, sizeof(sP_FE2CL_REP_PC_GOTO_SUCC), sock->getFEKey()));
|
||||
}
|
||||
|
||||
void PlayerManager::setSpecialPlayer(CNSocket* sock, CNPacketData* data) {
|
||||
sP_CL2FE_GM_REQ_PC_SET_VALUE* setData = (sP_CL2FE_GM_REQ_PC_SET_VALUE*)data->buf;
|
||||
sP_FE2CL_GM_REP_PC_SET_VALUE* response = (sP_FE2CL_GM_REP_PC_SET_VALUE*)xmalloc(sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE));
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "P_CL2FE_GM_REQ_PC_SET_VALUE:" << std::endl;
|
||||
std::cout << "\tPC_ID: " << setData->iPC_ID << std::endl;
|
||||
std::cout << "\tSetValueType: " << setData->iSetValueType << std::endl;
|
||||
std::cout << "\tSetValue: " << setData->iSetValue << std::endl;
|
||||
)
|
||||
|
||||
response->iPC_ID = setData->iPC_ID;
|
||||
response->iSetValue = setData->iSetValue;
|
||||
response->iSetValueType = setData->iSetValueType;
|
||||
|
||||
sock->sendPacket(new CNPacketData((void*)response, P_FE2CL_GM_REP_PC_SET_VALUE, sizeof(sP_FE2CL_GM_REP_PC_SET_VALUE), sock->getFEKey()));
|
||||
}
|
39
src/PlayerManager.hpp
Normal file
39
src/PlayerManager.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _PM_HPP
|
||||
#define _PM_HPP
|
||||
|
||||
#include "Player.hpp"
|
||||
#include "CNProtocol.hpp"
|
||||
#include "CNStructs.hpp"
|
||||
#include "CNShardServer.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
struct PlayerView {
|
||||
std::list<CNSocket*> viewable;
|
||||
Player plr;
|
||||
};
|
||||
|
||||
|
||||
namespace PlayerManager {
|
||||
extern std::map<CNSocket*, PlayerView> players;
|
||||
void init();
|
||||
|
||||
void addPlayer(CNSocket* key, Player plr);
|
||||
void removePlayer(CNSocket* key);
|
||||
Player getPlayer(CNSocket* key);
|
||||
|
||||
void updatePlayerPosition(CNSocket* sock, int X, int Y, int Z);
|
||||
|
||||
void enterPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void loadPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void movePlayer(CNSocket* sock, CNPacketData* data);
|
||||
void stopPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void jumpPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void movePlatformPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void gotoPlayer(CNSocket* sock, CNPacketData* data);
|
||||
void setSpecialPlayer(CNSocket* sock, CNPacketData* data);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
47
src/main.cpp
Normal file
47
src/main.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "CNLoginServer.hpp"
|
||||
#include "CNShardServer.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
#include "ChatManager.hpp"
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include "mingw/mingw.thread.h"
|
||||
#else
|
||||
#include <thread>
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
void startLogin(uint16_t port) {
|
||||
CNLoginServer server(port);
|
||||
server.start();
|
||||
}
|
||||
|
||||
void startShard(uint16_t port) {
|
||||
CNShardServer server(port);
|
||||
server.start();
|
||||
}
|
||||
|
||||
int main() {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
|
||||
std::cerr << "OpenFusion: WSAStartup failed" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
settings::init();
|
||||
std::cout << "[INFO] Intializing Packet Managers..." << std::endl;
|
||||
PlayerManager::init();
|
||||
ChatManager::init();
|
||||
|
||||
std::cout << "[INFO] Starting Server Threads..." << std::endl;
|
||||
std::thread loginThread(startLogin, settings::LOGINPORT);
|
||||
std::thread shardThread(startShard, settings::SHARDPORT);
|
||||
getchar(); // blocks until input
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
109
src/mingw/mingw.invoke.h
Normal file
109
src/mingw/mingw.invoke.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/// \file mingw.invoke.h
|
||||
/// \brief Lightweight `invoke` implementation, for C++11 and C++14.
|
||||
///
|
||||
/// (c) 2018-2019 by Nathaniel J. McClatchey, San Jose, CA, United States
|
||||
/// \author Nathaniel J. McClatchey, PhD
|
||||
///
|
||||
/// \copyright Simplified (2-clause) BSD License.
|
||||
///
|
||||
/// \note This file may become part of the mingw-w64 runtime package. If/when
|
||||
/// this happens, the appropriate license will be added, i.e. this code will
|
||||
/// become dual-licensed, and the current BSD 2-clause license will stay.
|
||||
|
||||
#ifndef MINGW_INVOKE_H_
|
||||
#define MINGW_INVOKE_H_
|
||||
|
||||
#include <type_traits> // For std::result_of, etc.
|
||||
#include <utility> // For std::forward
|
||||
#include <functional> // For std::reference_wrapper
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// For compatibility, implement std::invoke for C++11 and C++14
|
||||
#if __cplusplus < 201703L
|
||||
template<bool PMemFunc, bool PMemData>
|
||||
struct Invoker
|
||||
{
|
||||
template<class F, class... Args>
|
||||
inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args)
|
||||
{
|
||||
return std::forward<F>(f)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
template<bool>
|
||||
struct InvokerHelper;
|
||||
|
||||
template<>
|
||||
struct InvokerHelper<false>
|
||||
{
|
||||
template<class T1>
|
||||
inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1))
|
||||
{
|
||||
return *std::forward<T1>(t1);
|
||||
}
|
||||
|
||||
template<class T1>
|
||||
inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get())
|
||||
{
|
||||
return t1.get();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct InvokerHelper<true>
|
||||
{
|
||||
template<class T1>
|
||||
inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1))
|
||||
{
|
||||
return std::forward<T1>(t1);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Invoker<true, false>
|
||||
{
|
||||
template<class T, class F, class T1, class... Args>
|
||||
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
|
||||
decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...))
|
||||
{
|
||||
return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Invoker<false, true>
|
||||
{
|
||||
template<class T, class F, class T1, class... Args>
|
||||
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
|
||||
decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f)
|
||||
{
|
||||
return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;
|
||||
}
|
||||
};
|
||||
|
||||
template<class F, class... Args>
|
||||
struct InvokeResult
|
||||
{
|
||||
typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,
|
||||
std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&
|
||||
(sizeof...(Args) == 1)> invoker;
|
||||
inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...))
|
||||
{
|
||||
return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<class F, class...Args>
|
||||
auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...))
|
||||
{
|
||||
return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
using std::invoke;
|
||||
#endif
|
||||
} // Namespace "detail"
|
||||
} // Namespace "mingw_stdthread"
|
||||
|
||||
#endif
|
491
src/mingw/mingw.mutex.h
Normal file
491
src/mingw/mingw.mutex.h
Normal file
@@ -0,0 +1,491 @@
|
||||
/**
|
||||
* @file mingw.mutex.h
|
||||
* @brief std::mutex et al implementation for MinGW
|
||||
** (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this happens,
|
||||
* the appropriate license will be added, i.e. this code will become dual-licensed,
|
||||
* and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDMUTEX_H
|
||||
#define WIN32STDMUTEX_H
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
// Recursion checks on non-recursive locks have some performance penalty, and
|
||||
// the C++ standard does not mandate them. The user might want to explicitly
|
||||
// enable or disable such checks. If the user has no preference, enable such
|
||||
// checks in debug builds, but not in release builds.
|
||||
#ifdef STDMUTEX_RECURSION_CHECKS
|
||||
#elif defined(NDEBUG)
|
||||
#define STDMUTEX_RECURSION_CHECKS 0
|
||||
#else
|
||||
#define STDMUTEX_RECURSION_CHECKS 1
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <system_error>
|
||||
#include <atomic>
|
||||
#include <mutex> //need for call_once()
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG)
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
#include <sdkddkver.h> // Detect Windows version.
|
||||
|
||||
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
||||
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
|
||||
with Microsoft's API. We'll try to work around this, but we can make no\
|
||||
guarantees. This problem does not exist in MinGW-w64."
|
||||
#include <windows.h> // No further granularity can be expected.
|
||||
#else
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
#include <processthreadsapi.h> // For GetCurrentThreadId
|
||||
#endif
|
||||
#include <synchapi.h> // For InitializeCriticalSection, etc.
|
||||
#include <errhandlingapi.h> // For GetLastError
|
||||
#include <handleapi.h>
|
||||
#endif
|
||||
|
||||
// Need for the implementation of invoke
|
||||
#include "mingw.invoke.h"
|
||||
|
||||
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
|
||||
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
|
||||
#endif
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
// The _NonRecursive class has mechanisms that do not play nice with direct
|
||||
// manipulation of the native handle. This forward declaration is part of
|
||||
// a friend class declaration.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
namespace vista
|
||||
{
|
||||
class condition_variable;
|
||||
}
|
||||
#endif
|
||||
// To make this namespace equivalent to the thread-related subset of std,
|
||||
// pull in the classes and class templates supplied by std but not by this
|
||||
// implementation.
|
||||
using std::lock_guard;
|
||||
using std::unique_lock;
|
||||
using std::adopt_lock_t;
|
||||
using std::defer_lock_t;
|
||||
using std::try_to_lock_t;
|
||||
using std::adopt_lock;
|
||||
using std::defer_lock;
|
||||
using std::try_to_lock;
|
||||
|
||||
class recursive_mutex
|
||||
{
|
||||
CRITICAL_SECTION mHandle;
|
||||
public:
|
||||
typedef LPCRITICAL_SECTION native_handle_type;
|
||||
native_handle_type native_handle() {return &mHandle;}
|
||||
recursive_mutex() noexcept : mHandle()
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
}
|
||||
recursive_mutex (const recursive_mutex&) = delete;
|
||||
recursive_mutex& operator=(const recursive_mutex&) = delete;
|
||||
~recursive_mutex() noexcept
|
||||
{
|
||||
DeleteCriticalSection(&mHandle);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
EnterCriticalSection(&mHandle);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
LeaveCriticalSection(&mHandle);
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
return (TryEnterCriticalSection(&mHandle)!=0);
|
||||
}
|
||||
};
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
struct _OwnerThread
|
||||
{
|
||||
// If this is to be read before locking, then the owner-thread variable must
|
||||
// be atomic to prevent a torn read from spuriously causing errors.
|
||||
std::atomic<DWORD> mOwnerThread;
|
||||
constexpr _OwnerThread () noexcept : mOwnerThread(0) {}
|
||||
static void on_deadlock (void)
|
||||
{
|
||||
using namespace std;
|
||||
fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\
|
||||
detected. Throwing system exception\n");
|
||||
fflush(stderr);
|
||||
throw system_error(make_error_code(errc::resource_deadlock_would_occur));
|
||||
}
|
||||
DWORD checkOwnerBeforeLock() const
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if (mOwnerThread.load(std::memory_order_relaxed) == self)
|
||||
on_deadlock();
|
||||
return self;
|
||||
}
|
||||
void setOwnerAfterLock(DWORD id)
|
||||
{
|
||||
mOwnerThread.store(id, std::memory_order_relaxed);
|
||||
}
|
||||
void checkSetOwnerBeforeUnlock()
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if (mOwnerThread.load(std::memory_order_relaxed) != self)
|
||||
on_deadlock();
|
||||
mOwnerThread.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Though the Slim Reader-Writer (SRW) locks used here are not complete until
|
||||
// Windows 7, implementing partial functionality in Vista will simplify the
|
||||
// interaction with condition variables.
|
||||
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
|
||||
namespace windows7
|
||||
{
|
||||
class mutex
|
||||
{
|
||||
SRWLOCK mHandle;
|
||||
// Track locking thread for error checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread {};
|
||||
#endif
|
||||
public:
|
||||
typedef PSRWLOCK native_handle_type;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { }
|
||||
#pragma GCC diagnostic pop
|
||||
mutex (const mutex&) = delete;
|
||||
mutex & operator= (const mutex&) = delete;
|
||||
void lock (void)
|
||||
{
|
||||
// Note: Undefined behavior if called recursively.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
AcquireSRWLockExclusive(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
void unlock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
ReleaseSRWLockExclusive(&mHandle);
|
||||
}
|
||||
// TryAcquireSRW functions are a Windows 7 feature.
|
||||
#if (WINVER >= _WIN32_WINNT_WIN7)
|
||||
bool try_lock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
BOOL ret = TryAcquireSRWLockExclusive(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if (ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
native_handle_type native_handle (void)
|
||||
{
|
||||
return &mHandle;
|
||||
}
|
||||
};
|
||||
} // Namespace windows7
|
||||
#endif // Compiling for Vista
|
||||
namespace xp
|
||||
{
|
||||
class mutex
|
||||
{
|
||||
CRITICAL_SECTION mHandle;
|
||||
std::atomic_uchar mState;
|
||||
// Track locking thread for error checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread {};
|
||||
#endif
|
||||
public:
|
||||
typedef PCRITICAL_SECTION native_handle_type;
|
||||
constexpr mutex () noexcept : mHandle(), mState(2) { }
|
||||
mutex (const mutex&) = delete;
|
||||
mutex & operator= (const mutex&) = delete;
|
||||
~mutex() noexcept
|
||||
{
|
||||
// Undefined behavior if the mutex is held (locked) by any thread.
|
||||
// Undefined behavior if a thread terminates while holding ownership of the
|
||||
// mutex.
|
||||
DeleteCriticalSection(&mHandle);
|
||||
}
|
||||
void lock (void)
|
||||
{
|
||||
unsigned char state = mState.load(std::memory_order_acquire);
|
||||
while (state) {
|
||||
if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire))
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
mState.store(0, std::memory_order_release);
|
||||
break;
|
||||
}
|
||||
if (state == 1)
|
||||
{
|
||||
Sleep(0);
|
||||
state = mState.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
EnterCriticalSection(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
void unlock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
LeaveCriticalSection(&mHandle);
|
||||
}
|
||||
bool try_lock (void)
|
||||
{
|
||||
unsigned char state = mState.load(std::memory_order_acquire);
|
||||
if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire))
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
mState.store(0, std::memory_order_release);
|
||||
}
|
||||
if (state == 1)
|
||||
return false;
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
BOOL ret = TryEnterCriticalSection(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if (ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
native_handle_type native_handle (void)
|
||||
{
|
||||
return &mHandle;
|
||||
}
|
||||
};
|
||||
} // Namespace "xp"
|
||||
#if (WINVER >= _WIN32_WINNT_WIN7)
|
||||
using windows7::mutex;
|
||||
#else
|
||||
using xp::mutex;
|
||||
#endif
|
||||
|
||||
class recursive_timed_mutex
|
||||
{
|
||||
static constexpr DWORD kWaitAbandoned = 0x00000080l;
|
||||
static constexpr DWORD kWaitObject0 = 0x00000000l;
|
||||
static constexpr DWORD kInfinite = 0xffffffffl;
|
||||
inline bool try_lock_internal (DWORD ms) noexcept
|
||||
{
|
||||
DWORD ret = WaitForSingleObject(mHandle, ms);
|
||||
#ifndef NDEBUG
|
||||
if (ret == kWaitAbandoned)
|
||||
{
|
||||
using namespace std;
|
||||
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
|
||||
terminate();
|
||||
}
|
||||
#endif
|
||||
return (ret == kWaitObject0) || (ret == kWaitAbandoned);
|
||||
}
|
||||
protected:
|
||||
HANDLE mHandle;
|
||||
// Track locking thread for error checking of non-recursive timed_mutex. For
|
||||
// standard compliance, this must be defined in same class and at the same
|
||||
// access-control level as every other variable in the timed_mutex.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread {};
|
||||
#endif
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
native_handle_type native_handle() const {return mHandle;}
|
||||
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)) {}
|
||||
~recursive_timed_mutex()
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
DWORD ret = WaitForSingleObject(mHandle, kInfinite);
|
||||
// If (ret == WAIT_ABANDONED), then the thread that held ownership was
|
||||
// terminated. Behavior is undefined, but Windows will pass ownership to this
|
||||
// thread.
|
||||
#ifndef NDEBUG
|
||||
if (ret == kWaitAbandoned)
|
||||
{
|
||||
using namespace std;
|
||||
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
|
||||
terminate();
|
||||
}
|
||||
#endif
|
||||
if ((ret != kWaitObject0) && (ret != kWaitAbandoned))
|
||||
{
|
||||
throw std::system_error(GetLastError(), std::system_category());
|
||||
}
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
if (!ReleaseMutex(mHandle))
|
||||
throw std::system_error(GetLastError(), std::system_category());
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
return try_lock_internal(0);
|
||||
}
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto timeout = duration_cast<milliseconds>(dur).count();
|
||||
while (timeout > 0)
|
||||
{
|
||||
constexpr auto kMaxStep = static_cast<decltype(timeout)>(kInfinite-1);
|
||||
auto step = (timeout < kMaxStep) ? timeout : kMaxStep;
|
||||
if (try_lock_internal(static_cast<DWORD>(step)))
|
||||
return true;
|
||||
timeout -= step;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
|
||||
{
|
||||
return try_lock_for(timeout_time - Clock::now());
|
||||
}
|
||||
};
|
||||
|
||||
// Override if, and only if, it is necessary for error-checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
class timed_mutex: recursive_timed_mutex
|
||||
{
|
||||
public:
|
||||
timed_mutex(const timed_mutex&) = delete;
|
||||
timed_mutex& operator=(const timed_mutex&) = delete;
|
||||
void lock()
|
||||
{
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
recursive_timed_mutex::lock();
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
recursive_timed_mutex::unlock();
|
||||
}
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
|
||||
{
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
bool ret = recursive_timed_mutex::try_lock_for(dur);
|
||||
if (ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
return ret;
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
|
||||
{
|
||||
return try_lock_for(timeout_time - Clock::now());
|
||||
}
|
||||
bool try_lock ()
|
||||
{
|
||||
return try_lock_for(std::chrono::milliseconds(0));
|
||||
}
|
||||
};
|
||||
#else
|
||||
typedef recursive_timed_mutex timed_mutex;
|
||||
#endif
|
||||
|
||||
class once_flag
|
||||
{
|
||||
// When available, the SRW-based mutexes should be faster than the
|
||||
// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
|
||||
// and try_lock is not used by once_flag.
|
||||
#if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
|
||||
windows7::mutex mMutex;
|
||||
#else
|
||||
mutex mMutex;
|
||||
#endif
|
||||
std::atomic_bool mHasRun;
|
||||
once_flag(const once_flag&) = delete;
|
||||
once_flag& operator=(const once_flag&) = delete;
|
||||
template<class Callable, class... Args>
|
||||
friend void call_once(once_flag& once, Callable&& f, Args&&... args);
|
||||
public:
|
||||
constexpr once_flag() noexcept: mMutex(), mHasRun(false) {}
|
||||
};
|
||||
|
||||
template<class Callable, class... Args>
|
||||
void call_once(once_flag& flag, Callable&& func, Args&&... args)
|
||||
{
|
||||
if (flag.mHasRun.load(std::memory_order_acquire))
|
||||
return;
|
||||
lock_guard<decltype(flag.mMutex)> lock(flag.mMutex);
|
||||
if (flag.mHasRun.load(std::memory_order_acquire))
|
||||
return;
|
||||
detail::invoke(std::forward<Callable>(func),std::forward<Args>(args)...);
|
||||
flag.mHasRun.store(true, std::memory_order_release);
|
||||
}
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
// Push objects into std, but only if they are not already there.
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
using mingw_stdthread::recursive_mutex;
|
||||
using mingw_stdthread::mutex;
|
||||
using mingw_stdthread::recursive_timed_mutex;
|
||||
using mingw_stdthread::timed_mutex;
|
||||
using mingw_stdthread::once_flag;
|
||||
using mingw_stdthread::call_once;
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message "This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++11 std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
}
|
||||
#endif // WIN32STDMUTEX_H
|
503
src/mingw/mingw.shared_mutex.h
Normal file
503
src/mingw/mingw.shared_mutex.h
Normal file
@@ -0,0 +1,503 @@
|
||||
/// \file mingw.shared_mutex.h
|
||||
/// \brief Standard-compliant shared_mutex for MinGW
|
||||
///
|
||||
/// (c) 2017 by Nathaniel J. McClatchey, Athens OH, United States
|
||||
/// \author Nathaniel J. McClatchey
|
||||
///
|
||||
/// \copyright Simplified (2-clause) BSD License.
|
||||
///
|
||||
/// \note This file may become part of the mingw-w64 runtime package. If/when
|
||||
/// this happens, the appropriate license will be added, i.e. this code will
|
||||
/// become dual-licensed, and the current BSD 2-clause license will stay.
|
||||
/// \note Target Windows version is determined by WINVER, which is determined in
|
||||
/// <windows.h> from _WIN32_WINNT, which can itself be set by the user.
|
||||
|
||||
// Notes on the namespaces:
|
||||
// - The implementation can be accessed directly in the namespace
|
||||
// mingw_stdthread.
|
||||
// - Objects will be brought into namespace std by a using directive. This
|
||||
// will cause objects declared in std (such as MinGW's implementation) to
|
||||
// hide this implementation's definitions.
|
||||
// - To avoid poluting the namespace with implementation details, all objects
|
||||
// to be pushed into std will be placed in mingw_stdthread::visible.
|
||||
// The end result is that if MinGW supplies an object, it is automatically
|
||||
// used. If MinGW does not supply an object, this implementation's version will
|
||||
// instead be used.
|
||||
|
||||
#ifndef MINGW_SHARED_MUTEX_H_
|
||||
#define MINGW_SHARED_MUTEX_H_
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
// For descriptive errors.
|
||||
#include <system_error>
|
||||
// Implementing a shared_mutex without OS support will require atomic read-
|
||||
// modify-write capacity.
|
||||
#include <atomic>
|
||||
// For timing in shared_lock and shared_timed_mutex.
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
// Use MinGW's shared_lock class template, if it's available. Requires C++14.
|
||||
// If unavailable (eg. because this library is being used in C++11), then an
|
||||
// implementation of shared_lock is provided by this header.
|
||||
#if (__cplusplus >= 201402L)
|
||||
#include <shared_mutex>
|
||||
#endif
|
||||
|
||||
// For defer_lock_t, adopt_lock_t, and try_to_lock_t
|
||||
#include "mingw.mutex.h"
|
||||
// For this_thread::yield.
|
||||
//#include "mingw.thread.h"
|
||||
|
||||
// Might be able to use native Slim Reader-Writer (SRW) locks.
|
||||
#ifdef _WIN32
|
||||
#include <sdkddkver.h> // Detect Windows version.
|
||||
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
||||
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
|
||||
with Microsoft's API. We'll try to work around this, but we can make no\
|
||||
guarantees. This problem does not exist in MinGW-w64."
|
||||
#include <windows.h> // No further granularity can be expected.
|
||||
#else
|
||||
#include <synchapi.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
// Define a portable atomics-based shared_mutex
|
||||
namespace portable
|
||||
{
|
||||
class shared_mutex
|
||||
{
|
||||
typedef uint_fast16_t counter_type;
|
||||
std::atomic<counter_type> mCounter {0};
|
||||
static constexpr counter_type kWriteBit = 1 << (std::numeric_limits<counter_type>::digits - 1);
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
// Runtime checker for verifying owner threads. Note: Exclusive mode only.
|
||||
_OwnerThread mOwnerThread {};
|
||||
#endif
|
||||
public:
|
||||
typedef shared_mutex * native_handle_type;
|
||||
|
||||
shared_mutex () = default;
|
||||
|
||||
// No form of copying or moving should be allowed.
|
||||
shared_mutex (const shared_mutex&) = delete;
|
||||
shared_mutex & operator= (const shared_mutex&) = delete;
|
||||
|
||||
~shared_mutex ()
|
||||
{
|
||||
// Terminate if someone tries to destroy an owned mutex.
|
||||
assert(mCounter.load(std::memory_order_relaxed) == 0);
|
||||
}
|
||||
|
||||
void lock_shared (void)
|
||||
{
|
||||
counter_type expected = mCounter.load(std::memory_order_relaxed);
|
||||
do
|
||||
{
|
||||
// Delay if writing or if too many readers are attempting to read.
|
||||
if (expected >= kWriteBit - 1)
|
||||
{
|
||||
using namespace std;
|
||||
expected = mCounter.load(std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
if (mCounter.compare_exchange_weak(expected,
|
||||
static_cast<counter_type>(expected + 1),
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
|
||||
bool try_lock_shared (void)
|
||||
{
|
||||
counter_type expected = mCounter.load(std::memory_order_relaxed) & static_cast<counter_type>(~kWriteBit);
|
||||
if (expected + 1 == kWriteBit)
|
||||
return false;
|
||||
else
|
||||
return mCounter.compare_exchange_strong( expected,
|
||||
static_cast<counter_type>(expected + 1),
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void unlock_shared (void)
|
||||
{
|
||||
using namespace std;
|
||||
#ifndef NDEBUG
|
||||
if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast<counter_type>(~kWriteBit)))
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
#else
|
||||
mCounter.fetch_sub(1, memory_order_release);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Behavior is undefined if a lock was previously acquired.
|
||||
void lock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
using namespace std;
|
||||
// Might be able to use relaxed memory order...
|
||||
// Wait for the write-lock to be unlocked, then claim the write slot.
|
||||
counter_type current;
|
||||
while ((current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire)) & kWriteBit);
|
||||
//this_thread::yield();
|
||||
// Wait for readers to finish up.
|
||||
while (current != kWriteBit)
|
||||
{
|
||||
//this_thread::yield();
|
||||
current = mCounter.load(std::memory_order_acquire);
|
||||
}
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool try_lock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
counter_type expected = 0;
|
||||
bool ret = mCounter.compare_exchange_strong(expected, kWriteBit,
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if (ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unlock (void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
using namespace std;
|
||||
#ifndef NDEBUG
|
||||
if (mCounter.load(memory_order_relaxed) != kWriteBit)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
#endif
|
||||
mCounter.store(0, memory_order_release);
|
||||
}
|
||||
|
||||
native_handle_type native_handle (void)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace portable
|
||||
|
||||
// The native shared_mutex implementation primarily uses features of Windows
|
||||
// Vista, but the features used for try_lock and try_lock_shared were not
|
||||
// introduced until Windows 7. To allow limited use while compiling for Vista,
|
||||
// I define the class without try_* functions in that case.
|
||||
// Only fully-featured implementations will be placed into namespace std.
|
||||
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
|
||||
namespace vista
|
||||
{
|
||||
class condition_variable_any;
|
||||
}
|
||||
|
||||
namespace windows7
|
||||
{
|
||||
// We already #include "mingw.mutex.h". May as well reduce redundancy.
|
||||
class shared_mutex : windows7::mutex
|
||||
{
|
||||
// Allow condition_variable_any (and only condition_variable_any) to treat a
|
||||
// shared_mutex as its base class.
|
||||
friend class vista::condition_variable_any;
|
||||
public:
|
||||
using windows7::mutex::native_handle_type;
|
||||
using windows7::mutex::lock;
|
||||
using windows7::mutex::unlock;
|
||||
using windows7::mutex::native_handle;
|
||||
|
||||
void lock_shared (void)
|
||||
{
|
||||
AcquireSRWLockShared(native_handle());
|
||||
}
|
||||
|
||||
void unlock_shared (void)
|
||||
{
|
||||
ReleaseSRWLockShared(native_handle());
|
||||
}
|
||||
|
||||
// TryAcquireSRW functions are a Windows 7 feature.
|
||||
#if (WINVER >= _WIN32_WINNT_WIN7)
|
||||
bool try_lock_shared (void)
|
||||
{
|
||||
return TryAcquireSRWLockShared(native_handle()) != 0;
|
||||
}
|
||||
|
||||
using windows7::mutex::try_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // Namespace windows7
|
||||
#endif // Compiling for Vista
|
||||
#if (defined(_WIN32) && (WINVER >= _WIN32_WINNT_WIN7))
|
||||
using windows7::shared_mutex;
|
||||
#else
|
||||
using portable::shared_mutex;
|
||||
#endif
|
||||
|
||||
class shared_timed_mutex : shared_mutex
|
||||
{
|
||||
typedef shared_mutex Base;
|
||||
public:
|
||||
using Base::lock;
|
||||
using Base::try_lock;
|
||||
using Base::unlock;
|
||||
using Base::lock_shared;
|
||||
using Base::try_lock_shared;
|
||||
using Base::unlock_shared;
|
||||
|
||||
template< class Clock, class Duration >
|
||||
bool try_lock_until ( const std::chrono::time_point<Clock,Duration>& cutoff )
|
||||
{
|
||||
do
|
||||
{
|
||||
if (try_lock())
|
||||
return true;
|
||||
}
|
||||
while (std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template< class Rep, class Period >
|
||||
bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time)
|
||||
{
|
||||
return try_lock_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
bool try_lock_shared_until ( const std::chrono::time_point<Clock,Duration>& cutoff )
|
||||
{
|
||||
do
|
||||
{
|
||||
if (try_lock_shared())
|
||||
return true;
|
||||
}
|
||||
while (std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template< class Rep, class Period >
|
||||
bool try_lock_shared_for (const std::chrono::duration<Rep,Period>& rel_time)
|
||||
{
|
||||
return try_lock_shared_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
};
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
using std::shared_lock;
|
||||
#else
|
||||
// If not supplied by shared_mutex (eg. because C++14 is not supported), I
|
||||
// supply the various helper classes that the header should have defined.
|
||||
template<class Mutex>
|
||||
class shared_lock
|
||||
{
|
||||
Mutex * mMutex;
|
||||
bool mOwns;
|
||||
// Reduce code redundancy
|
||||
void verify_lockable (void)
|
||||
{
|
||||
using namespace std;
|
||||
if (mMutex == nullptr)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
if (mOwns)
|
||||
throw system_error(make_error_code(errc::resource_deadlock_would_occur));
|
||||
}
|
||||
public:
|
||||
typedef Mutex mutex_type;
|
||||
|
||||
shared_lock (void) noexcept
|
||||
: mMutex(nullptr), mOwns(false)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock (shared_lock<Mutex> && other) noexcept
|
||||
: mMutex(other.mutex_), mOwns(other.owns_)
|
||||
{
|
||||
other.mMutex = nullptr;
|
||||
other.mOwns = false;
|
||||
}
|
||||
|
||||
explicit shared_lock (mutex_type & m)
|
||||
: mMutex(&m), mOwns(true)
|
||||
{
|
||||
mMutex->lock_shared();
|
||||
}
|
||||
|
||||
shared_lock (mutex_type & m, defer_lock_t) noexcept
|
||||
: mMutex(&m), mOwns(false)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock (mutex_type & m, adopt_lock_t)
|
||||
: mMutex(&m), mOwns(true)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock (mutex_type & m, try_to_lock_t)
|
||||
: mMutex(&m), mOwns(m.try_lock_shared())
|
||||
{
|
||||
}
|
||||
|
||||
template< class Rep, class Period >
|
||||
shared_lock( mutex_type& m, const std::chrono::duration<Rep,Period>& timeout_duration )
|
||||
: mMutex(&m), mOwns(m.try_lock_shared_for(timeout_duration))
|
||||
{
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
shared_lock( mutex_type& m, const std::chrono::time_point<Clock,Duration>& timeout_time )
|
||||
: mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time))
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock& operator= (shared_lock<Mutex> && other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
if (mOwns)
|
||||
mMutex->unlock_shared();
|
||||
mMutex = other.mMutex;
|
||||
mOwns = other.mOwns;
|
||||
other.mMutex = nullptr;
|
||||
other.mOwns = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
~shared_lock (void)
|
||||
{
|
||||
if (mOwns)
|
||||
mMutex->unlock_shared();
|
||||
}
|
||||
|
||||
shared_lock (const shared_lock<Mutex> &) = delete;
|
||||
shared_lock& operator= (const shared_lock<Mutex> &) = delete;
|
||||
|
||||
// Shared locking
|
||||
void lock (void)
|
||||
{
|
||||
verify_lockable();
|
||||
mMutex->lock_shared();
|
||||
mOwns = true;
|
||||
}
|
||||
|
||||
bool try_lock (void)
|
||||
{
|
||||
verify_lockable();
|
||||
mOwns = mMutex->try_lock_shared();
|
||||
return mOwns;
|
||||
}
|
||||
|
||||
template< class Clock, class Duration >
|
||||
bool try_lock_until( const std::chrono::time_point<Clock,Duration>& cutoff )
|
||||
{
|
||||
verify_lockable();
|
||||
do
|
||||
{
|
||||
mOwns = mMutex->try_lock_shared();
|
||||
if (mOwns)
|
||||
return mOwns;
|
||||
}
|
||||
while (std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template< class Rep, class Period >
|
||||
bool try_lock_for (const std::chrono::duration<Rep,Period>& rel_time)
|
||||
{
|
||||
return try_lock_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
|
||||
void unlock (void)
|
||||
{
|
||||
using namespace std;
|
||||
if (!mOwns)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
mMutex->unlock_shared();
|
||||
mOwns = false;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
void swap (shared_lock<Mutex> & other) noexcept
|
||||
{
|
||||
using namespace std;
|
||||
swap(mMutex, other.mMutex);
|
||||
swap(mOwns, other.mOwns);
|
||||
}
|
||||
|
||||
mutex_type * release (void) noexcept
|
||||
{
|
||||
mutex_type * ptr = mMutex;
|
||||
mMutex = nullptr;
|
||||
mOwns = false;
|
||||
return ptr;
|
||||
}
|
||||
// Observers
|
||||
mutex_type * mutex (void) const noexcept
|
||||
{
|
||||
return mMutex;
|
||||
}
|
||||
|
||||
bool owns_lock (void) const noexcept
|
||||
{
|
||||
return mOwns;
|
||||
}
|
||||
|
||||
explicit operator bool () const noexcept
|
||||
{
|
||||
return owns_lock();
|
||||
}
|
||||
};
|
||||
|
||||
template< class Mutex >
|
||||
void swap( shared_lock<Mutex>& lhs, shared_lock<Mutex>& rhs ) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
#endif // C++11
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if (__cplusplus < 201703L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS))
|
||||
using mingw_stdthread::shared_mutex;
|
||||
#endif
|
||||
#if (__cplusplus < 201402L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS))
|
||||
using mingw_stdthread::shared_timed_mutex;
|
||||
using mingw_stdthread::shared_lock;
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message "This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++ std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
} // Namespace std
|
||||
#endif // MINGW_SHARED_MUTEX_H_
|
360
src/mingw/mingw.thread.h
Normal file
360
src/mingw/mingw.thread.h
Normal file
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* @file mingw.thread.h
|
||||
* @brief std::thread implementation for MinGW
|
||||
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this happens,
|
||||
* the appropriate license will be added, i.e. this code will become dual-licensed,
|
||||
* and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDTHREAD_H
|
||||
#define WIN32STDTHREAD_H
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
|
||||
// Use the standard classes for std::, if available.
|
||||
#include <thread>
|
||||
|
||||
#include <cstddef> // For std::size_t
|
||||
#include <cerrno> // Detect error type.
|
||||
#include <exception> // For std::terminate
|
||||
#include <system_error> // For std::system_error
|
||||
#include <functional> // For std::hash
|
||||
#include <tuple> // For std::tuple
|
||||
#include <chrono> // For sleep timing.
|
||||
#include <memory> // For std::unique_ptr
|
||||
#include <iosfwd> // Stream output for thread ids.
|
||||
#include <utility> // For std::swap, std::forward
|
||||
|
||||
#include "mingw.invoke.h"
|
||||
|
||||
#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
||||
#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\
|
||||
with Microsoft's API. We'll try to work around this, but we can make no\
|
||||
guarantees. This problem does not exist in MinGW-w64."
|
||||
#include <windows.h> // No further granularity can be expected.
|
||||
#else
|
||||
#include <synchapi.h> // For WaitForSingleObject
|
||||
#include <handleapi.h> // For CloseHandle, etc.
|
||||
#include <sysinfoapi.h> // For GetNativeSystemInfo
|
||||
#include <processthreadsapi.h> // For GetCurrentThreadId
|
||||
#endif
|
||||
#include <process.h> // For _beginthreadex
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
|
||||
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
|
||||
#endif
|
||||
|
||||
// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0.
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template<std::size_t...>
|
||||
struct IntSeq {};
|
||||
|
||||
template<std::size_t N, std::size_t... S>
|
||||
struct GenIntSeq : GenIntSeq<N-1, N-1, S...> { };
|
||||
|
||||
template<std::size_t... S>
|
||||
struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; };
|
||||
|
||||
// Use a template specialization to avoid relying on compiler optimization
|
||||
// when determining the parameter integer sequence.
|
||||
template<class Func, class T, typename... Args>
|
||||
class ThreadFuncCall;
|
||||
// We can't define the Call struct in the function - the standard forbids template methods in that case
|
||||
template<class Func, std::size_t... S, typename... Args>
|
||||
class ThreadFuncCall<Func, detail::IntSeq<S...>, Args...>
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(Args), "Args must match.");
|
||||
using Tuple = std::tuple<typename std::decay<Args>::type...>;
|
||||
typename std::decay<Func>::type mFunc;
|
||||
Tuple mArgs;
|
||||
|
||||
public:
|
||||
ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
|
||||
: mFunc(std::forward<Func>(aFunc)),
|
||||
mArgs(std::forward<Args>(aArgs)...)
|
||||
{
|
||||
}
|
||||
|
||||
void callFunc()
|
||||
{
|
||||
detail::invoke(std::move(mFunc), std::move(std::get<S>(mArgs)) ...);
|
||||
}
|
||||
};
|
||||
|
||||
// Allow construction of threads without exposing implementation.
|
||||
class ThreadIdTool;
|
||||
} // Namespace "detail"
|
||||
|
||||
class thread
|
||||
{
|
||||
public:
|
||||
class id
|
||||
{
|
||||
DWORD mId = 0;
|
||||
friend class thread;
|
||||
friend class std::hash<id>;
|
||||
friend class detail::ThreadIdTool;
|
||||
explicit id(DWORD aId) noexcept : mId(aId){}
|
||||
public:
|
||||
id (void) noexcept = default;
|
||||
friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; }
|
||||
friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; }
|
||||
friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; }
|
||||
friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; }
|
||||
friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; }
|
||||
friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; }
|
||||
|
||||
template<class _CharT, class _Traits>
|
||||
friend std::basic_ostream<_CharT, _Traits>&
|
||||
operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id)
|
||||
{
|
||||
if (__id.mId == 0)
|
||||
{
|
||||
return __out << "(invalid std::thread::id)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return __out << __id.mId;
|
||||
}
|
||||
}
|
||||
};
|
||||
private:
|
||||
static constexpr HANDLE kInvalidHandle = nullptr;
|
||||
static constexpr DWORD kInfinite = 0xffffffffl;
|
||||
HANDLE mHandle;
|
||||
id mThreadId;
|
||||
|
||||
template <class Call>
|
||||
static unsigned __stdcall threadfunc(void* arg)
|
||||
{
|
||||
std::unique_ptr<Call> call(static_cast<Call*>(arg));
|
||||
call->callFunc();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int _hardware_concurrency_helper() noexcept
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
// This is one of the few functions used by the library which has a nearly-
|
||||
// equivalent function defined in earlier versions of Windows. Include the
|
||||
// workaround, just as a reminder that it does exist.
|
||||
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
|
||||
::GetNativeSystemInfo(&sysinfo);
|
||||
#else
|
||||
::GetSystemInfo(&sysinfo);
|
||||
#endif
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
}
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
id get_id() const noexcept {return mThreadId;}
|
||||
native_handle_type native_handle() const {return mHandle;}
|
||||
thread(): mHandle(kInvalidHandle), mThreadId(){}
|
||||
|
||||
thread(thread&& other)
|
||||
:mHandle(other.mHandle), mThreadId(other.mThreadId)
|
||||
{
|
||||
other.mHandle = kInvalidHandle;
|
||||
other.mThreadId = id{};
|
||||
}
|
||||
|
||||
thread(const thread &other)=delete;
|
||||
|
||||
template<class Func, typename... Args>
|
||||
explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
|
||||
{
|
||||
using ArgSequence = typename detail::GenIntSeq<sizeof...(Args)>::type;
|
||||
using Call = detail::ThreadFuncCall<Func, ArgSequence, Args...>;
|
||||
auto call = new Call(
|
||||
std::forward<Func>(func), std::forward<Args>(args)...);
|
||||
unsigned id_receiver;
|
||||
auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>,
|
||||
static_cast<LPVOID>(call), 0, &id_receiver);
|
||||
if (int_handle == 0)
|
||||
{
|
||||
mHandle = kInvalidHandle;
|
||||
int errnum = errno;
|
||||
delete call;
|
||||
// Note: Should only throw EINVAL, EAGAIN, EACCES
|
||||
throw std::system_error(errnum, std::generic_category());
|
||||
} else {
|
||||
mThreadId.mId = id_receiver;
|
||||
mHandle = reinterpret_cast<HANDLE>(int_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool joinable() const {return mHandle != kInvalidHandle;}
|
||||
|
||||
// Note: Due to lack of synchronization, this function has a race condition
|
||||
// if called concurrently, which leads to undefined behavior. The same applies
|
||||
// to all other member functions of this class, but this one is mentioned
|
||||
// explicitly.
|
||||
void join()
|
||||
{
|
||||
using namespace std;
|
||||
if (get_id() == id(GetCurrentThreadId()))
|
||||
throw system_error(make_error_code(errc::resource_deadlock_would_occur));
|
||||
if (mHandle == kInvalidHandle)
|
||||
throw system_error(make_error_code(errc::no_such_process));
|
||||
if (!joinable())
|
||||
throw system_error(make_error_code(errc::invalid_argument));
|
||||
WaitForSingleObject(mHandle, kInfinite);
|
||||
CloseHandle(mHandle);
|
||||
mHandle = kInvalidHandle;
|
||||
mThreadId = id{};
|
||||
}
|
||||
|
||||
~thread()
|
||||
{
|
||||
if (joinable())
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::printf("Error: Must join() or detach() a thread before \
|
||||
destroying it.\n");
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
thread& operator=(const thread&) = delete;
|
||||
thread& operator=(thread&& other) noexcept
|
||||
{
|
||||
if (joinable())
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::printf("Error: Must join() or detach() a thread before \
|
||||
moving another thread to it.\n");
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
swap(std::forward<thread>(other));
|
||||
return *this;
|
||||
}
|
||||
void swap(thread&& other) noexcept
|
||||
{
|
||||
std::swap(mHandle, other.mHandle);
|
||||
std::swap(mThreadId.mId, other.mThreadId.mId);
|
||||
}
|
||||
|
||||
static unsigned int hardware_concurrency() noexcept
|
||||
{
|
||||
static unsigned int cached = _hardware_concurrency_helper();
|
||||
return cached;
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
if (!joinable())
|
||||
{
|
||||
using namespace std;
|
||||
throw system_error(make_error_code(errc::invalid_argument));
|
||||
}
|
||||
if (mHandle != kInvalidHandle)
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
mHandle = kInvalidHandle;
|
||||
}
|
||||
mThreadId = id{};
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
class ThreadIdTool
|
||||
{
|
||||
public:
|
||||
static thread::id make_id (DWORD base_id) noexcept
|
||||
{
|
||||
return thread::id(base_id);
|
||||
}
|
||||
};
|
||||
} // Namespace "detail"
|
||||
|
||||
namespace this_thread
|
||||
{
|
||||
inline thread::id get_id() noexcept
|
||||
{
|
||||
return detail::ThreadIdTool::make_id(GetCurrentThreadId());
|
||||
}
|
||||
inline void yield() noexcept {Sleep(0);}
|
||||
template< class Rep, class Period >
|
||||
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
|
||||
{
|
||||
static constexpr DWORD kInfinite = 0xffffffffl;
|
||||
using namespace std::chrono;
|
||||
using rep = milliseconds::rep;
|
||||
rep ms = duration_cast<milliseconds>(sleep_duration).count();
|
||||
while (ms > 0)
|
||||
{
|
||||
constexpr rep kMaxRep = static_cast<rep>(kInfinite - 1);
|
||||
auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep;
|
||||
Sleep(static_cast<DWORD>(sleepTime));
|
||||
ms -= sleepTime;
|
||||
}
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
|
||||
{
|
||||
sleep_for(sleep_time-Clock::now());
|
||||
}
|
||||
}
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
using mingw_stdthread::thread;
|
||||
// Remove ambiguity immediately, to avoid problems arising from the above.
|
||||
//using std::thread;
|
||||
namespace this_thread
|
||||
{
|
||||
using namespace mingw_stdthread::this_thread;
|
||||
}
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message "This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++11 std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
|
||||
// Specialize hash for this implementation's thread::id, even if the
|
||||
// std::thread::id already has a hash.
|
||||
template<>
|
||||
struct hash<mingw_stdthread::thread::id>
|
||||
{
|
||||
typedef mingw_stdthread::thread::id argument_type;
|
||||
typedef size_t result_type;
|
||||
size_t operator() (const argument_type & i) const noexcept
|
||||
{
|
||||
return i.mId;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif // WIN32STDTHREAD_H
|
40
src/settings.cpp
Normal file
40
src/settings.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <iostream>
|
||||
#include "settings.hpp"
|
||||
#include "INIReader.hpp"
|
||||
|
||||
// defaults :)
|
||||
int settings::LOGINPORT = 8001;
|
||||
bool settings::LOGINRANDCHARACTERS = false;
|
||||
|
||||
int settings::SHARDPORT = 8002;
|
||||
std::string settings::SHARDSERVERIP = "127.0.0.1";
|
||||
int settings::VIEWDISTANCE = 25000;
|
||||
|
||||
// default spawn point is city hall
|
||||
int settings::SPAWN_X = 179213;
|
||||
int settings::SPAWN_Y = 268451;
|
||||
int settings::SPAWN_Z = -4210;
|
||||
|
||||
|
||||
void settings::init() {
|
||||
INIReader reader("config.ini");
|
||||
|
||||
if (reader.ParseError() != 0) {
|
||||
if (reader.ParseError() == -1)
|
||||
std::cerr << "[WARN] Settings: missing config.ini file!" << std::endl;
|
||||
else
|
||||
std::cerr << "[WARN] Settings: invalid config.ini syntax at line " << reader.ParseError() << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
|
||||
LOGINRANDCHARACTERS = reader.GetBoolean("login", "randomcharacters", LOGINRANDCHARACTERS);
|
||||
SHARDPORT = reader.GetInteger("shard", "port", SHARDPORT);
|
||||
SHARDSERVERIP = reader.Get("shard", "ip", "127.0.0.1");
|
||||
VIEWDISTANCE = reader.GetInteger("shard", "view", VIEWDISTANCE);
|
||||
SPAWN_X = reader.GetInteger("shard", "spawnx", SPAWN_X);
|
||||
SPAWN_Y = reader.GetInteger("shard", "spawny", SPAWN_Y);
|
||||
SPAWN_Z = reader.GetInteger("shard", "spawnz", SPAWN_Z);
|
||||
|
||||
}
|
17
src/settings.hpp
Normal file
17
src/settings.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _SETT_HPP
|
||||
#define _SETT_HPP
|
||||
|
||||
namespace settings {
|
||||
extern int LOGINPORT;
|
||||
extern bool LOGINRANDCHARACTERS;
|
||||
extern int SHARDPORT;
|
||||
extern std::string SHARDSERVERIP;
|
||||
extern int VIEWDISTANCE;
|
||||
extern int SPAWN_X;
|
||||
extern int SPAWN_Y;
|
||||
extern int SPAWN_Z;
|
||||
|
||||
void init();
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user