Server update 0.63.5

- async lua implementation
- cleaner backend heartbeat
- two way encryption on connect
- async tcp buffer
- disconnect handler
- cleaned UDP implementation
This commit is contained in:
Anonymous275 2020-10-16 17:05:31 +03:00
parent 31c96cee94
commit 71a84b4f1b
14 changed files with 314 additions and 169 deletions

42
include/Buffer.h Normal file
View File

@ -0,0 +1,42 @@
///
/// Created by Anonymous275 on 8/25/2020
///
#pragma once
#include <mutex>
class Client;
void GParser(Client*c, const std::string&Packet);
class Buffer{
public:
void Handle(Client*c,const std::string& Data){
if(c == nullptr)return;
Buf += Data;
Manage(c);
}
void clear(){
Buf.clear();
}
private:
std::string Buf;
void Manage(Client*c){
if(!Buf.empty()){
std::string::size_type p;
if (Buf.at(0) == '\n'){
p = Buf.find('\n',1);
if(p != -1){
std::string R = Buf.substr(1,p-1);
std::string_view B(R.c_str(),R.find(char(0)));
GParser(c, B.data());
Buf = Buf.substr(p+1);
Manage(c);
}
}else{
p = Buf.find('\n');
if(p == -1)Buf.clear();
else{
Buf = Buf.substr(p);
Manage(c);
}
}
}
}
};

View File

@ -4,6 +4,7 @@
#pragma once
#include <WS2tcpip.h>
#include "Buffer.h"
#include <string>
#include <vector>
#include <chrono>
@ -48,6 +49,7 @@ public:
int GetCarCount();
void ClearCars();
int GetStatus();
Buffer Handler;
int GetID();
};
struct ClientInterface{

View File

@ -8,6 +8,7 @@
#include "lua.hpp"
#include <vector>
#include <thread>
#include <mutex>
#include <set>
#include <any>
namespace fs = std::experimental::filesystem;
@ -50,17 +51,15 @@ public:
void SetPluginName(const std::string&Name);
void SetFileName(const std::string&Name);
fs::file_time_type GetLastWrite();
bool isThreadExecuting = false;
std::string GetPluginName();
std::string GetFileName();
bool isExecuting = false;
bool StopThread = false;
bool HasThread = false;
lua_State* GetState();
char* GetOrigin();
std::mutex Lock;
void Reload();
void Init();
};
int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* args);
int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg);
int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg,bool Wait);
extern std::set<Lua*> PluginEngine;

View File

@ -12,8 +12,8 @@ void SyncResources(Client*c);
[[noreturn]] void UDPServerMain();
void OnDisconnect(Client*c,bool kicked);
void UDPSend(Client*c,std::string Data);
void TCPSend(Client*c,const std::string&Data);
void SendLarge(Client*c,const std::string&Data);
void TCPSend(Client*c,const std::string& Data);
void SendLarge(Client*c,std::string Data);
void GParser(Client*c, const std::string&Packet);
void Respond(Client*c, const std::string& MSG, bool Rel);
void SendToAll(Client*c, const std::string& Data, bool Self, bool Rel);

View File

@ -10,6 +10,7 @@ struct RSA{
int e = 0;
int d = 0;
};
std::string RSA_E(const std::string& Data,int e, int n);
std::string RSA_E(const std::string& Data, RSA*k);
std::string RSA_D(const std::string& Data, RSA*k);
int Handle(EXCEPTION_POINTERS *ep,char* Origin);

View File

@ -100,7 +100,13 @@ std::string RSA_E(const std::string& Data, RSA*k){
}
return stream.str();
}
std::string RSA_E(const std::string& Data,int e, int n){
std::stringstream stream;
for(const char&c : Data){
stream << std::hex << Enc(uint8_t(c),e,n) << "g";
}
return stream.str();
}
std::string RSA_D(const std::string& Data, RSA*k){
std::stringstream ss(Data);
std::string token,ret;

View File

@ -19,28 +19,28 @@ std::string GetPlayers(){
return Return;
}
std::string GenerateCall(){
std::string State = Private ? Sec("true") : Sec("false");
std::string ret = Sec("uuid=");
ret += Key+Sec("&players=")+std::to_string(CI->Size())+Sec("&maxplayers=")+std::to_string(MaxPlayers)+Sec("&port=")
+ std::to_string(Port) + Sec("&map=") + MapName + Sec("&private=")+State+Sec("&version=")+GetSVer()+
Sec("&clientversion=")+GetCVer()+Sec("&name=")+ServerName+Sec("&pps=")+StatReport+Sec("&modlist=")+FileList+
Sec("&modstotalsize=")+std::to_string(MaxModSize)+Sec("&modstotal=")+std::to_string(ModsLoaded)
+Sec("&playerslist=")+GetPlayers()+Sec("&desc=")+ServerDesc;
std::string State = Private ? "true" : "false";
std::string ret = "uuid=";
ret += Key+"&players="+std::to_string(CI->Size())+"&maxplayers="+std::to_string(MaxPlayers)+"&port="
+ std::to_string(Port) + "&map=" + MapName + "&private="+State+"&version="+GetSVer()+
"&clientversion="+GetCVer()+"&name="+ServerName+"&pps="+StatReport+"&modlist="+FileList+
"&modstotalsize="+std::to_string(MaxModSize)+"&modstotal="+std::to_string(ModsLoaded)
+"&playerslist="+GetPlayers()+"&desc="+ServerDesc;
return ret;
}
void Heartbeat(){
std::string R,T;
while(true){
R = GenerateCall();
if(!CustomIP.empty())R+=Sec("&ip=")+CustomIP;
//https://beamng-mp.com/heartbeatv2
std::string link = Sec("https://beamng-mp.com/heartbeatv2");
if(!CustomIP.empty())R+="&ip="+CustomIP;
std::string link = Sec("https://beammp.com/heartbeatv2");
T = PostHTTP(link,R);
if(T.find_first_not_of(Sec("20")) != std::string::npos){
//Backend system refused server startup!
std::this_thread::sleep_for(std::chrono::seconds(10));
T = PostHTTP(link,R);
if(T.find_first_not_of(Sec("20")) != std::string::npos){
std::string Backup = Sec("https://backup1.beammp.com/heartbeatv2");
T = PostHTTP(Backup,R);
if(T.find_first_not_of(Sec("20")) != std::string::npos) {
error(Sec("Backend system refused server! Check your AuthKey"));
std::this_thread::sleep_for(std::chrono::seconds(3));
exit(-1);

View File

@ -8,10 +8,12 @@
std::string CustomIP;
std::string GetSVer(){
return Sec("0.60");
static std::string r = Sec("0.63.5");
return r;
}
std::string GetCVer(){
return Sec("1.60");
static std::string r = Sec("1.63");
return r;
}
void Args(int argc, char* argv[]){
info(Sec("BeamMP Server Running version ") + GetSVer());

View File

@ -9,6 +9,7 @@
#include "Logger.h"
#include <iostream>
#include <future>
#include <utility>
LuaArg* CreateArg(lua_State *L,int T,int S){
if(S > T)return nullptr;
@ -26,6 +27,9 @@ LuaArg* CreateArg(lua_State *L,int T,int S){
}
return temp;
}
void ClearStack(lua_State *L){
lua_settop(L,0);
}
Lua* GetScript(lua_State *L){
for(Lua*Script : PluginEngine){
if (Script->GetState() == L)return Script;
@ -35,33 +39,41 @@ Lua* GetScript(lua_State *L){
void SendError(lua_State *L,const std::string&msg){
Lua* S = GetScript(L);
std::string a = S->GetFileName().substr(S->GetFileName().find('\\'));
warn(a + Sec(" | Incorrect Call of ") +msg);
warn(a + Sec(" | Incorrect Call of ") + msg);
}
int Trigger(Lua*lua,const std::string& R, LuaArg*arg){
std::lock_guard<std::mutex> lockGuard(lua->Lock);
std::packaged_task<int()> task([lua,R,arg]{return CallFunction(lua,R,arg);});
std::future<int> f1 = task.get_future();
std::thread t(std::move(task));
t.detach();
auto status = f1.wait_for(std::chrono::seconds(3));
auto status = f1.wait_for(std::chrono::seconds(5));
if(status != std::future_status::timeout)return f1.get();
SendError(lua->GetState(),R + " took too long to respond");
return 0;
}
int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg){
int FutureWait(Lua*lua,const std::string& R, LuaArg*arg,bool Wait){
std::packaged_task<int()> task([lua,R,arg]{return Trigger(lua,R,arg);});
std::future<int> f1 = task.get_future();
std::thread t(std::move(task));
t.detach();
int T = 0;
if(Wait)T = 6;
auto status = f1.wait_for(std::chrono::seconds(T));
if(status != std::future_status::timeout)return f1.get();
return 0;
}
int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg,bool Wait){
int R = 0;
for(Lua*Script : PluginEngine){
if(Script->IsRegistered(Event)){
if(local){
if (Script->GetPluginName() == Caller->GetPluginName()){
R += Trigger(Script,Script->GetRegistered(Event),arg);
R += FutureWait(Script,Script->GetRegistered(Event),arg,Wait);
}
}else R += Trigger(Script,Script->GetRegistered(Event), arg);
}else R += FutureWait(Script,Script->GetRegistered(Event), arg,Wait);
}
}
if(arg != nullptr){
delete arg;
arg = nullptr;
}
return R;
}
@ -70,7 +82,7 @@ bool CheckLua(lua_State *L, int r){
std::string msg = lua_tostring(L, -1);
Lua * S = GetScript(L);
std::string a = S->GetFileName().substr(S->GetFileName().find('\\'));
warn(a + " | at line " + msg.substr(msg.find(':')+1));
warn(a + " | " + msg);
return false;
}
return true;
@ -89,7 +101,7 @@ int lua_TriggerEventL(lua_State *L){
Lua* Script = GetScript(L);
if(Args > 0){
if(lua_isstring(L,1)){
TriggerLuaEvent(lua_tostring(L, 1), true, Script, CreateArg(L,Args,2));
TriggerLuaEvent(lua_tostring(L, 1), true, Script, CreateArg(L,Args,2),false);
}else SendError(L,Sec("TriggerLocalEvent wrong argument [1] need string"));
}else{
SendError(L,Sec("TriggerLocalEvent not enough arguments expected 1 got 0"));
@ -102,7 +114,7 @@ int lua_TriggerEventG(lua_State *L){
Lua* Script = GetScript(L);
if(Args > 0){
if(lua_isstring(L,1)) {
TriggerLuaEvent(lua_tostring(L, 1), false, Script, CreateArg(L,Args,2));
TriggerLuaEvent(lua_tostring(L, 1), false, Script, CreateArg(L,Args,2),false);
}else SendError(L,Sec("TriggerGlobalEvent wrong argument [1] need string"));
}else SendError(L,Sec("TriggerGlobalEvent not enough arguments"));
return 0;
@ -110,24 +122,12 @@ int lua_TriggerEventG(lua_State *L){
char* ThreadOrigin(Lua*lua){
std::string T = "Thread in " + lua->GetFileName().substr(lua->GetFileName().find('\\'));
char* Data = new char[T.size()];
ZeroMemory(Data,T.size());
char* Data = new char[T.size()+1];
ZeroMemory(Data,T.size()+1);
memcpy_s(Data,T.size(),T.c_str(),T.size());
return Data;
}
void Lock(Lua* lua,bool thread){
bool Lock;
do{
if(thread){
Lock = lua->isExecuting;
}else Lock = lua->isThreadExecuting;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}while(Lock);
}
void ExecuteAsync(Lua* lua,const std::string& FuncName){
Lock(lua,true);
lua->isThreadExecuting = true;
void SafeExecution(Lua* lua,const std::string& FuncName){
lua_State* luaState = lua->GetState();
lua_getglobal(luaState, FuncName.c_str());
if(lua_isfunction(luaState, -1)) {
@ -138,21 +138,20 @@ void ExecuteAsync(Lua* lua,const std::string& FuncName){
}__except(Handle(GetExceptionInformation(),Origin)){}
delete [] Origin;
}
lua->isThreadExecuting = false;
ClearStack(luaState);
}
void ExecuteAsync(Lua* lua,const std::string& FuncName){
std::lock_guard<std::mutex> lockGuard(lua->Lock);
SafeExecution(lua,FuncName);
}
void CallAsync(Lua* lua,const std::string& Func,int U){
if(lua->HasThread){
SendError(lua->GetState(),Sec("CreateThread : There is already a thread running!"));
return;
}
lua->StopThread = false;
lua->HasThread = true;
int D = 1000 / U;
while(!lua->StopThread){
ExecuteAsync(lua,Func);
std::this_thread::sleep_for(std::chrono::milliseconds(D));
}
lua->HasThread = false;
}
int lua_StopThread(lua_State *L){
GetScript(L)->StopThread = true;
@ -329,10 +328,43 @@ int lua_HWID(lua_State *L){
lua_pushinteger(L, -1);
return 1;
}
int lua_RemoteEvent(lua_State *L){
int Args = lua_gettop(L);
if(Args != 3){
SendError(L,Sec("TriggerClientEvent invalid argument count expected 3 got ") + std::to_string(Args));
return 0;
}
if(!lua_isnumber(L,1)){
SendError(L,Sec("TriggerClientEvent invalid argument [1] expected number"));
return 0;
}
if(!lua_isstring(L,2)){
SendError(L,Sec("TriggerClientEvent invalid argument [2] expected string"));
return 0;
}
if(!lua_isstring(L,3)){
SendError(L,Sec("TriggerClientEvent invalid argument [3] expected string"));
return 0;
}
int ID = int(lua_tointeger(L,1));
std::string Packet = "E:" + std::string(lua_tostring(L,2)) + ":" + std::string(lua_tostring(L,3));
if(ID == -1){
SendToAll(nullptr,Packet,true,true);
}else{
Client *c = GetClient(ID);
if(c == nullptr){
SendError(L,Sec("TriggerClientEvent invalid Player ID"));
return 0;
}
Respond(c,Packet,true);
}
return 0;
}
void Lua::Init(){
luaL_openlibs(luaState);
lua_register(luaState,"TriggerGlobalEvent",lua_TriggerEventG);
lua_register(luaState,"TriggerLocalEvent",lua_TriggerEventL);
lua_register(luaState,"TriggerClientEvent",lua_RemoteEvent);
lua_register(luaState,"GetPlayerCount",lua_GetPlayerCount);
lua_register(luaState,"isPlayerConnected",lua_isConnected);
lua_register(luaState,"RegisterEvent",lua_RegisterEvent);
@ -352,7 +384,7 @@ void Lua::Init(){
void Lua::Reload(){
if(CheckLua(luaState,luaL_dofile(luaState,FileName.c_str()))){
CallFunction(this,Sec("onInit"),{});
CallFunction(this,Sec("onInit"), nullptr);
}
}
char* Lua::GetOrigin(){
@ -363,8 +395,6 @@ char* Lua::GetOrigin(){
return Data;
}
int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){
Lock(lua,false);
lua->isExecuting = true;
lua_State*luaState = lua->GetState();
lua_getglobal(luaState, FuncName.c_str());
if(lua_isfunction(luaState, -1)) {
@ -372,6 +402,8 @@ int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){
if(Arg != nullptr){
Size = int(Arg->args.size());
Arg->PushArgs(luaState);
delete Arg;
Arg = nullptr;
}
int R = 0;
char* Origin = lua->GetOrigin();
@ -385,7 +417,7 @@ int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){
}__except(Handle(GetExceptionInformation(),Origin)){}
delete [] Origin;
}
lua->isExecuting = false;
ClearStack(luaState);
return 0;
}
void Lua::SetPluginName(const std::string&Name){

View File

@ -29,8 +29,10 @@ std::string Rcv(SOCKET TCPSock){
}
std::string GetRole(const std::string &DID){
if(!DID.empty()){
std::string a = HttpRequest(Sec("https://beamng-mp.com/entitlement?did=")+DID,443);
if(!a.empty()){
std::string a = HttpRequest(Sec("https://beammp.com/entitlement?did=")+DID,443);
std::string b = HttpRequest(Sec("https://backup1.beammp.com/entitlement?did=")+DID,443);
if(!a.empty() || !b.empty()){
if(a != b)a = b;
auto pos = a.find('"');
if(pos != std::string::npos){
return a.substr(pos+1,a.find('"',pos+1)-2);
@ -63,26 +65,46 @@ void CreateClient(SOCKET TCPSock,const std::string &Name, const std::string &DID
CI->AddClient(c);
InitClient(c);
}
std::pair<int,int> Parse(const std::string& msg){
std::stringstream ss(msg);
std::string t;
std::pair<int,int> a = {0,0}; //N then E
while (std::getline(ss, t, 'g')) {
if(t.find_first_not_of(Sec("0123456789abcdef")) != std::string::npos)return a;
if(a.first == 0){
a.first = std::stoi(t, nullptr, 16);
}else if(a.second == 0){
a.second = std::stoi(t, nullptr, 16);
}else return a;
}
return {0,0};
}
std::string GenerateM(RSA*key){
std::stringstream stream;
stream << std::hex << key->n << "g" << key->e << "g" << RSA_E(Sec("IDC"),key);
return stream.str();
}
void Identification(SOCKET TCPSock,Hold*S,RSA*key){
void Identification(SOCKET TCPSock,Hold*S,RSA*Skey){
S->TCPSock = TCPSock;
std::thread Timeout(Check,S);
Timeout.detach();
std::string Name,DID,Role;
if(!Send(TCPSock,GenerateM(key))){
if(!Send(TCPSock,GenerateM(Skey))){
closesocket(TCPSock);
return;
}
std::string msg = Rcv(TCPSock);
auto Keys = Parse(msg);
if(!Send(TCPSock,RSA_E("HC",Keys.second,Keys.first))){
closesocket(TCPSock);
return;
}
std::string Res = Rcv(TCPSock);
std::string Ver = Rcv(TCPSock);
S->Done = true;
Ver = RSA_D(Ver,key);
Ver = RSA_D(Ver,Skey);
if(Ver.size() > 3 && Ver.substr(0,2) == Sec("VC")){
Ver = Ver.substr(2);
if(Ver.length() > 4 || Ver != GetCVer()){
@ -93,7 +115,7 @@ void Identification(SOCKET TCPSock,Hold*S,RSA*key){
closesocket(TCPSock);
return;
}
Res = RSA_D(Res,key);
Res = RSA_D(Res,Skey);
if(Res.size() < 3 || Res.substr(0,2) != Sec("NR")) {
closesocket(TCPSock);
return;
@ -125,11 +147,15 @@ void Identification(SOCKET TCPSock,Hold*S,RSA*key){
}
void Identify(SOCKET TCPSock){
auto* S = new Hold;
RSA*key = GenKey();
RSA*Skey = GenKey();
__try{
Identification(TCPSock,S,key);
}__except(1){}
delete key;
Identification(TCPSock,S,Skey);
}__except(1){
if(TCPSock != -1){
closesocket(TCPSock);
}
}
delete Skey;
delete S;
}
void TCPServerMain(){

View File

@ -7,6 +7,7 @@
#include "Settings.h"
#include "Network.h"
#include "Logger.h"
#include <sstream>
int FC(const std::string& s,const std::string& p,int n) {
auto i = s.find(p);
@ -42,7 +43,7 @@ void VehicleParser(Client*c,const std::string& Pckt){
Packet = "Os:"+c->GetRole()+":"+c->GetName()+":"+std::to_string(c->GetID())+"-"+std::to_string(CarID)+Packet.substr(4);
if(c->GetCarCount() >= MaxCars ||
TriggerLuaEvent(Sec("onVehicleSpawn"),false,nullptr,
new LuaArg{{c->GetID(),CarID,Packet.substr(3)}})){
new LuaArg{{c->GetID(),CarID,Packet.substr(3)}},true)){
Respond(c,Packet,true);
std::string Destroy = "Od:" + std::to_string(c->GetID())+"-"+std::to_string(CarID);
Respond(c,Destroy,true);
@ -62,7 +63,7 @@ void VehicleParser(Client*c,const std::string& Pckt){
}
if(PID != -1 && VID != -1 && PID == c->GetID()){
if(!TriggerLuaEvent(Sec("onVehicleEdited"),false,nullptr,
new LuaArg{{c->GetID(),VID,Packet.substr(3)}})) {
new LuaArg{{c->GetID(),VID,Packet.substr(3)}},true)) {
SendToAll(c, Packet, false, true);
Apply(c,VID,Packet);
}else{
@ -82,7 +83,7 @@ void VehicleParser(Client*c,const std::string& Pckt){
if(PID != -1 && VID != -1 && PID == c->GetID()){
SendToAll(nullptr,Packet,true,true);
TriggerLuaEvent(Sec("onVehicleDeleted"),false,nullptr,
new LuaArg{{c->GetID(),VID}});
new LuaArg{{c->GetID(),VID}},false);
c->DeleteCar(VID);
debug(c->GetName() + Sec(" deleted car with ID ") + std::to_string(VID));
}
@ -96,19 +97,23 @@ void VehicleParser(Client*c,const std::string& Pckt){
}
void SyncClient(Client*c){
if(c->isSynced)return;
c->isSynced = true;
std::this_thread::sleep_for(std::chrono::seconds(1));
Respond(c,Sec("Sn")+c->GetName(),true);
SendToAll(c,Sec("JWelcome ")+c->GetName()+"!",false,true);
TriggerLuaEvent(Sec("onPlayerJoin"),false,nullptr,new LuaArg{{c->GetID()}});
TriggerLuaEvent(Sec("onPlayerJoin"),false,nullptr,new LuaArg{{c->GetID()}},false);
for (Client*client : CI->Clients) {
if(client != nullptr){
if (client != c) {
for (VData *v : client->GetAllCars()) {
Respond(c, v->Data, true);
if(v != nullptr){
Respond(c, v->Data, true);
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
}
}
}
c->isSynced = true;
info(c->GetName() + Sec(" is now synced!"));
}
void ParseVeh(Client*c, const std::string&Packet){
@ -117,9 +122,30 @@ void ParseVeh(Client*c, const std::string&Packet){
}__except(Handle(GetExceptionInformation(),Sec("Vehicle Handler"))){}
}
void GlobalParser(Client*c, const std::string& Packet){
void HandleEvent(Client*c ,const std::string&Data){
std::stringstream ss(Data);
std::string t,Name;
int a = 0;
while (std::getline(ss, t, ':')) {
switch(a){
case 1:
Name = t;
break;
case 2:
TriggerLuaEvent(Name, false, nullptr,new LuaArg{{c->GetID(),t}},false);
break;
default:
break;
}
if(a == 2)break;
a++;
}
}
void GlobalParser(Client*c, const std::string& Pack){
static int lastRecv = 0;
if(Packet.empty() || c == nullptr)return;
if(Pack.empty() || c == nullptr)return;
std::string Packet = Pack.substr(0,Pack.find(char(0)));
std::string pct;
char Code = Packet.at(0);
@ -150,14 +176,13 @@ void GlobalParser(Client*c, const std::string& Packet){
return;
case 'C':
if(Packet.length() < 4 || Packet.find(':', 3) == -1)break;
pct = "C:" + c->GetName() + Packet.substr(Packet.find(':', 3));
if (TriggerLuaEvent(Sec("onChatMessage"), false, nullptr,
new LuaArg{{c->GetID(), c->GetName(), pct.substr(pct.find(':', 3) + 1)}}))break;
SendToAll(nullptr, pct, true, true);
pct.clear();
if (TriggerLuaEvent(Sec("onChatMessage"), false, nullptr,new LuaArg{
{c->GetID(), c->GetName(), Packet.substr(Packet.find(':', 3) + 1)}
},true))break;
SendToAll(nullptr, Packet, true, true);
return;
case 'E':
SendToAll(nullptr,Packet,true,true);
HandleEvent(c,Packet);
return;
default:
return;

View File

@ -25,7 +25,7 @@ int OpenID(){
}
void Respond(Client*c, const std::string& MSG, bool Rel){
char C = MSG.at(0);
if(Rel){
if(Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E'){
if(C == 'O' || C == 'T' || MSG.length() > 1000)SendLarge(c,MSG);
else TCPSend(c,MSG);
}else UDPSend(c,MSG);
@ -35,9 +35,10 @@ void SendToAll(Client*c, const std::string& Data, bool Self, bool Rel){
for(Client*client : CI->Clients){
if(client != nullptr) {
if (Self || client != c) {
if (client->isSynced) {
if (Rel) {
if (C == 'O' || C == 'T' || Data.length() > 1000)SendLarge(client, Data);
if (client->isSynced || (C == 'O' && Data.at(1) == 's')) {
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
if (C == 'O' || C == 'T' ||
Data.length() > 1000)SendLarge(client, Data);
else TCPSend(client, Data);
} else UDPSend(client, Data);
}
@ -54,6 +55,7 @@ void UpdatePlayers(){
SendToAll(nullptr, Packet,true,true);
}
void OnDisconnect(Client*c,bool kicked){
info(c->GetName() + Sec(" Connection Terminated"));
if(c == nullptr)return;
std::string Packet;
for(VData*v : c->GetAllCars()){
@ -66,17 +68,18 @@ void OnDisconnect(Client*c,bool kicked){
Packet = Sec("L")+c->GetName()+Sec(" Left the server!");
SendToAll(c, Packet,false,true);
Packet.clear();
TriggerLuaEvent(Sec("onPlayerDisconnect"),false,nullptr,new LuaArg{{c->GetID()}});
TriggerLuaEvent(Sec("onPlayerDisconnect"),false,nullptr,new LuaArg{{c->GetID()}},false);
c->ClearCars();
CI->RemoveClient(c); ///Removes the Client from existence
}
void OnConnect(Client*c){
info(Sec("Client connected"));
c->SetID(OpenID());
info(Sec("Assigned ID ") + std::to_string(c->GetID()) + Sec(" to ") + c->GetName());
TriggerLuaEvent(Sec("onPlayerConnecting"),false,nullptr,new LuaArg{{c->GetID()}});
TriggerLuaEvent(Sec("onPlayerConnecting"),false,nullptr,new LuaArg{{c->GetID()}},false);
SyncResources(c);
if(c->GetStatus() < 0)return;
Respond(c,"M"+MapName,true); //Send the Map on connect
info(c->GetName() + Sec(" : Connected"));
TriggerLuaEvent(Sec("onPlayerJoining"),false,nullptr,new LuaArg{{c->GetID()}});
TriggerLuaEvent(Sec("onPlayerJoining"),false,nullptr,new LuaArg{{c->GetID()}},false);
}

View File

@ -8,16 +8,24 @@
void TCPSend(Client*c,const std::string&Data){
if(c == nullptr)return;
int BytesSent = send(c->GetTCPSock(), Data.c_str(), int(Data.length())+1, 0);
if (BytesSent == 0){
std::string Send = "\n" + Data.substr(0,Data.find(char(0))) + "\n";
size_t Sent = send(c->GetTCPSock(), Send.c_str(), int(Send.size()), 0);
if (Sent == 0){
if(c->GetStatus() > -1)c->SetStatus(-1);
}else if (BytesSent < 0) {
}else if (Sent < 0) {
if(c->GetStatus() > -1)c->SetStatus(-1);
closesocket(c->GetTCPSock());
}
}
void TCPHandle(Client*c,const std::string& data){
__try{
c->Handler.Handle(c,data);
}__except(1){
c->Handler.clear();
}
}
void TCPRcv(Client*c){
if(c == nullptr)return;
if(c == nullptr || c->GetStatus() < 0)return;
char buf[4096];
int len = 4096;
ZeroMemory(buf, len);
@ -33,18 +41,18 @@ void TCPRcv(Client*c){
return;
}
std::string Buf(buf,BytesRcv);
GParser(c, Buf);
TCPHandle(c,Buf);
}
void TCPClient(Client*c){
if(c->GetTCPSock() == -1){
CI->RemoveClient(c);
return;
}
info(Sec("Client connected"));
OnConnect(c);
while (c->GetStatus() > -1)TCPRcv(c);
info(c->GetName() + Sec(" Connection Terminated"));
OnDisconnect(c, c->GetStatus() == -2);
__try{
OnDisconnect(c, c->GetStatus() == -2);
}__except(Handle(GetExceptionInformation(),Sec("OnDisconnect"))){}
}
void InitClient(Client*c){
std::thread NewClient(TCPClient,c);

View File

@ -8,9 +8,11 @@
#include "Settings.h"
#include "Network.h"
#include "Logger.h"
#include <sstream>
#include <vector>
#include <thread>
#include <array>
int FC(const std::string& s,const std::string& p,int n);
struct PacketData{
int ID;
Client* Client;
@ -30,11 +32,11 @@ void UDPSend(Client*c,std::string Data){
if(c == nullptr || !c->isConnected || c->GetStatus() < 0)return;
sockaddr_in Addr = c->GetUDPAddr();
int AddrSize = sizeof(c->GetUDPAddr());
Data = Data.substr(0,Data.find(char(0)));
if(Data.length() > 400){
std::string CMP(Comp(Data));
Data = "ABG:" + CMP;
}
int sendOk = sendto(UDPSock, Data.c_str(), int(Data.size()), 0, (sockaddr *) &Addr, AddrSize);
if (sendOk == SOCKET_ERROR) {
debug(Sec("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
@ -44,7 +46,7 @@ void UDPSend(Client*c,std::string Data){
void AckID(int ID){
for(PacketData* p : DataAcks){
if(p->ID == ID){
if(p != nullptr && p->ID == ID){
DataAcks.erase(p);
break;
}
@ -62,7 +64,8 @@ int SplitID(){
else SID++;
return SID;
}
void SendLarge(Client*c,const std::string&Data){
void SendLarge(Client*c,std::string Data){
Data = Data.substr(0,Data.find(char(0)));
int ID = PacktID();
std::string Packet;
if(Data.length() > 1000){
@ -70,16 +73,16 @@ void SendLarge(Client*c,const std::string&Data){
int S = 1,Split = int(ceil(float(pckt.length()) / 1000));
int SID = SplitID();
while(pckt.length() > 1000){
Packet = "SC"+std::to_string(S)+"/"+std::to_string(Split)+":"+std::to_string(ID)+"|"+
std::to_string(SID)+":"+pckt.substr(0,1000);
Packet = "SC|"+std::to_string(S)+"|"+std::to_string(Split)+"|"+std::to_string(ID)+"|"+
std::to_string(SID)+"|"+pckt.substr(0,1000);
DataAcks.insert(new PacketData{ID,c,Packet,1});
UDPSend(c,Packet);
pckt = pckt.substr(1000);
S++;
ID = PacktID();
}
Packet = "SC"+std::to_string(S)+"/"+std::to_string(Split)+":"+
std::to_string(ID)+"|"+std::to_string(SID)+":"+pckt;
Packet = "SC|"+std::to_string(S)+"|"+std::to_string(Split)+"|"+
std::to_string(ID)+"|"+std::to_string(SID)+"|"+pckt;
DataAcks.insert(new PacketData{ID,c,Packet,1});
UDPSend(c,Packet);
}else{
@ -90,12 +93,12 @@ void SendLarge(Client*c,const std::string&Data){
}
struct HandledC{
int Pos = 0;
Client *c{};
std::array<int, 50> HandledIDs{};
Client *c = nullptr;
std::array<int, 100> HandledIDs = {-1};
};
std::set<HandledC*> HandledIDs;
void ResetIDs(HandledC*H){
for(int C = 0;C < 50;C++){
for(int C = 0;C < 100;C++){
H->HandledIDs.at(C) = -1;
}
}
@ -114,7 +117,7 @@ bool Handled(Client*c,int ID){
for(int id : h->HandledIDs){
if(id == ID)return true;
}
if(h->Pos > 49)h->Pos = 0;
if(h->Pos > 99)h->Pos = 0;
h->HandledIDs.at(h->Pos) = ID;
h->Pos++;
handle = true;
@ -129,7 +132,7 @@ bool Handled(Client*c,int ID){
if(!handle){
HandledC *h = GetHandled(c);
ResetIDs(h);
if (h->Pos > 49)h->Pos = 0;
if (h->Pos > 99)h->Pos = 0;
h->HandledIDs.at(h->Pos) = ID;
h->Pos++;
h->c = c;
@ -138,17 +141,14 @@ bool Handled(Client*c,int ID){
return false;
}
std::string UDPRcvFromClient(sockaddr_in& client){
char buf[10240];
int clientLength = sizeof(client);
ZeroMemory(&client, clientLength);
ZeroMemory(buf, 10240);
int Rcv = recvfrom(UDPSock, buf, 10240, 0, (sockaddr*)&client, &clientLength);
std::string Ret(10240,0);
int Rcv = recvfrom(UDPSock, &Ret[0], 10240, 0, (sockaddr*)&client, &clientLength);
if (Rcv == -1){
error(Sec("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
return "";
}
std::string Ret(Rcv,0);
memcpy_s(&Ret[0],Rcv,buf,Rcv);
return Ret;
}
@ -161,22 +161,27 @@ SplitData*GetSplit(int SplitID){
return SP;
}
void HandleChunk(Client*c,const std::string&Data){
int pos1 = int(Data.find(':'))+1,
pos2 = int(Data.find(':',pos1)),
pos3 = int(Data.find('/')),
pos4 = int(Data.find('|'));
if(pos1 == std::string::npos)return;
int Max = stoi(Data.substr(pos3+1,pos1-pos3-2));
int Current = stoi(Data.substr(2,pos3-2));
int ID = stoi(Data.substr(pos1,pos4-pos1));
int SplitID = stoi(Data.substr(pos4+1,pos2-pos4-1));
std::string ack = Sec("TRG:") + Data.substr(pos1,pos4-pos1);
int pos = FC(Data,"|",5);
if(pos == -1)return;
std::stringstream ss(Data.substr(0,pos++));
std::string t;
int I = -1;
//Current Max ID SID
std::vector<int> Num(4,0);
while (std::getline(ss, t, '|')) {
if(I != -1)Num.at(I) = std::stoi(t);
I++;
}
std::string ack = "TRG:" + std::to_string(Num.at(2));
UDPSend(c,ack);
if(Handled(c,ID))return;
SplitData* SData = GetSplit(SplitID);
SData->Total = Max;
SData->ID = SplitID;
SData->Fragments.insert(std::make_pair(Current,Data.substr(pos2+1)));
if(Handled(c,Num.at(2))){
return;
}
std::string Packet = Data.substr(pos);
SplitData* SData = GetSplit(Num.at(3));
SData->Total = Num.at(1);
SData->ID = Num.at(3);
SData->Fragments.insert(std::make_pair(Num.at(0),Packet));
if(SData->Fragments.size() == SData->Total){
std::string ToHandle;
for(const std::pair<int,std::string>& a : SData->Fragments){
@ -184,36 +189,56 @@ void HandleChunk(Client*c,const std::string&Data){
}
GParser(c,ToHandle);
SplitPackets.erase(SData);
delete SData;
SData = nullptr;
}
}
void UDPParser(Client*c,std::string Packet){
if(Packet.substr(0,4) == Sec("ABG:")){
if(Packet.substr(0,4) == "ABG:"){
Packet = DeComp(Packet.substr(4));
}
if(Packet.substr(0,4) == Sec("TRG:")){
if(Packet.substr(0,4) == "TRG:"){
std::string pkt = Packet.substr(4);
if(Packet.find_first_not_of("0123456789") == -1){
AckID(stoi(Packet));
}
return;
}else if(Packet.substr(0,3) == Sec("BD:")){
}else if(Packet.substr(0,3) == "BD:"){
auto pos = Packet.find(':',4);
int ID = stoi(Packet.substr(3,pos-3));
std::string pkt = Sec("TRG:") + std::to_string(ID);
std::string pkt = "TRG:" + std::to_string(ID);
UDPSend(c,pkt);
if(!Handled(c,ID)) {
pkt = Packet.substr(pos + 1);
GParser(c, pkt);
}
return;
}else if(Packet.substr(0,2) == Sec("SC")){
}else if(Packet.substr(0,2) == "SC"){
HandleChunk(c,Packet);
return;
}
GParser(c,Packet);
}
#include <thread>
void StartLoop();
void LOOP(){
while(UDPSock != -1) {
for (PacketData* p : DataAcks){
if(p != nullptr) {
if (p->Client == nullptr || p->Client->GetTCPSock() == -1) {
DataAcks.erase(p);
break;
}
if (p->Tries < 15) {
UDPSend(p->Client, p->Data);
p->Tries++;
} else {
DataAcks.erase(p);
break;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
}
[[noreturn]] void UDPServerMain(){
WSADATA data;
if (WSAStartup(514, &data)){
@ -237,7 +262,8 @@ void StartLoop();
}
DataAcks.clear();
StartLoop();
std::thread Ack(LOOP);
Ack.detach();
info(Sec("Vehicle data network online on port ")+std::to_string(Port)+Sec(" with a Max of ")+std::to_string(MaxPlayers)+Sec(" Clients"));
while (true){
@ -253,38 +279,11 @@ void StartLoop();
if(c != nullptr && c->GetID() == ID){
c->SetUDPAddr(client);
c->isConnected = true;
std::thread Parse(UDPParser,c,Data.substr(2));
Parse.detach();
UDPParser(c,Data.substr(2));
}
}
}
/*closesocket(UDPSock);
WSACleanup();
return;*/
}
void LOOP(){
while(UDPSock != -1) {
for (PacketData* p : DataAcks){
if(p != nullptr) {
if (p->Client == nullptr || p->Client->GetTCPSock() == -1) {
DataAcks.erase(p);
break;
}
if (p->Tries < 20) {
UDPSend(p->Client, p->Data);
p->Tries++;
} else {
DataAcks.erase(p);
delete p;
p = nullptr;
break;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
void StartLoop(){
std::thread Ack(LOOP);
Ack.detach();
}
}