#include "MessageIdentifiers.h" #include "RakNetworkFactory.h" #include "RakPeerInterface.h" #include "RakNetStatistics.h" #include "RakNetTypes.h" #include "FileListTransfer.h" #include "FileListTransferCBInterface.h" #include "FileOperations.h" #include "SuperFastHash.h" #include "RakAssert.h" #include "IncrementalReadInterface.h" #include "BitStream.h" #include "RakSleep.h" #include #include #include #include #include #include #include #include #include "connect.h" #include "types.h" #include "GameSettings.h" #include "message.h" #include "FileMan.h" #include "INIReader.h" #include #include "transfer_rules.h" #include "MPJoinScreen.h" #include "Game Init.h" #include "Text.h" #include "network.h" #include "message.h" #include "Overhead.h" #include "fresh_header.h" #include "Debug Control.h" #include "MPXmlTeams.hpp" extern CHAR16 gzFileTransferDirectory[100]; // WANNE: FILE TRANSFER unsigned int setID; // Sender progress notification class ServerFileListProgress : public FileListProgress { virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem) { // WANNE: Removed output in strategy log screen, because otherwise we do not see chat messages easily /* if (done) { char* filename = ExtractFilename((char*)fileName); ScreenMsg( FONT_RED, MSG_MPSYSTEM, MPServerMessage[10], filename); } */ } char *ExtractFilename(char *pathname) { char *s; if ((s=strrchr(pathname, '\\')) != NULL) s++; else if ((s=strrchr(pathname, '/')) != NULL) s++; else if ((s=strrchr(pathname, ':')) != NULL) s++; else s = pathname; return s; } } serverFileListProgress; // ------------------------------ // Global Server Variables from ja2_mp.ini // ------------------------------ char gKitBag[100]; UINT8 gMaxMercs; UINT8 gDisableMorale; UINT8 gReportHiredMerc; UINT8 gDisableBobbyRay; UINT8 gDisableMercEquipment; UINT8 gSameMercAllowed; float gDamageMultiplier; UINT8 gMaxClients ; UINT8 gGameType; UINT8 gDifficultyLevel; UINT8 gSkillTraits; UINT8 gSyncGameDirectory; INT32 gSecondsPerTick; INT32 gStartingCash; float gStartingTime; UINT8 gWeaponReadyBonus; UINT8 gInventoryAttachment; UINT8 gDisableSpectatorMode; UINT8 gMaxEnemiesEnabled; // ------------------------------ UINT16 nc; //number of open connection UINT16 ncr; //number of ready confirmed connections //something keep record of ready connections .. int mercs_ready[255]; unsigned char SGetPacketIdentifier(Packet *p); unsigned char SpacketIdentifier; RakPeerInterface *server; // WANNE: FILE TRANSFER FileListTransfer fltServer; // flt1 IncrementalReadInterface incrementalReadInterface; FileList fileList; // OJW - 20090405 long fileListTotalBytes=0; int numreadyteams; int readyteamreg[10]; bool Sawarded; SystemAddress blank; typedef struct { SystemAddress address; int cl_number; }client_data; client_data client_d[4]; int client_mercteam[4] = { 0 , 1 , 2 , 3 }; // random index of random_merc_teams per player bool inline can_joingame(); char *ReplaceCharactersInString_Server(char *str, char *orig, char *rep) { static char buffer[4096]; char *p; if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'? return str; strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$ buffer[p-str] = '\0'; sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig)); return buffer; } int f_rec_num(int mode, SystemAddress sender)//from client data { int x; client_data cl_record; for ( x=0; x<4;x++) { cl_record = client_d[x]; if(mode==0)//find empty slot for new record { if(cl_record.address.binaryAddress==0) return(x); } if(mode==1)//wipe clean all { client_d[x].address.binaryAddress=0; client_d[x].address.port=0; client_d[x].cl_number=0; } if(mode==2)//clear one record { if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) { client_d[x].address.port=0; client_d[x].cl_number=0; client_d[x].address.binaryAddress=0; return(254); } } // OJW - 090212 - look up client number if (mode == 3) { if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) { return (x); } } } if(mode == 0)//'no free slots' { ScreenMsg( FONT_RED, MSG_MPSYSTEM, L"Client Record Error, Restart Server, and Report Error." ); return (255); } return(254); } // use UNASSIGNED_SYSTEM_ADDRESS instead of rpcParameters->sender to send it back to yourself (the sender) // there is very little in here dependant on the game engine and originally started out as an independant dedicated server .exe, and could if go ther again ... hayden. //********* RPC SECTION ************ void sendPATH(RPCParameters *rpcParameters) { server->RPC("recievePATH",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } // OJW - 20090405 void sendDOWNLOADSTATUS(RPCParameters *rpcParameters) { server->RPC("recieveDOWNLOADSTATUS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendSTANCE(RPCParameters *rpcParameters) { server->RPC("recieveSTANCE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDIR(RPCParameters *rpcParameters) { server->RPC("recieveDIR",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendFIRE(RPCParameters *rpcParameters) { server->RPC("recieveFIRE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendHIT(RPCParameters *rpcParameters) { EV_S_WEAPONHIT* hit = (EV_S_WEAPONHIT*)rpcParameters->input; int team = MercPtrs[ hit->ubAttackerID ]->bTeam; // AI if (team == 1) team = 4; // Client else if (team >= 6) team -= 6; else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution Assert(team<5); // FIXME gMPPlayerStats[team].hits++; server->RPC("recieveHIT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDISMISS(RPCParameters *rpcParameters) { server->RPC("recieveDISMISS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendHIRE(RPCParameters *rpcParameters) { server->RPC("recieveHIRE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendguiPOS(RPCParameters *rpcParameters) { server->RPC("recieveguiPOS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendguiDIR(RPCParameters *rpcParameters) { server->RPC("recieveguiDIR",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendEndTurn(RPCParameters *rpcParameters) { server->RPC("recieveEndTurn",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendAI(RPCParameters *rpcParameters) { server->RPC("recieveAI",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendSTOP(RPCParameters *rpcParameters) { server->RPC("recieveSTOP",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendINTERRUPT(RPCParameters *rpcParameters) { server->RPC("recieveINTERRUPT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendREADY(RPCParameters *rpcParameters) { server->RPC("recieveREADY",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendGUI(RPCParameters *rpcParameters) { server->RPC("recieveGUI",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendBULLET(RPCParameters *rpcParameters) { server->RPC("recieveBULLET",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendGRENADE(RPCParameters *rpcParameters) { server->RPC("recieveGRENADE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendGRENADERESULT(RPCParameters *rpcParameters) { server->RPC("recieveGRENADERESULT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendPLANTEXPLOSIVE(RPCParameters *rpcParameters) { server->RPC("recievePLANTEXPLOSIVE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDETONATEEXPLOSIVE(RPCParameters *rpcParameters) { server->RPC("recieveDETONATEEXPLOSIVE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDISARMEXPLOSIVE(RPCParameters *rpcParameters) { server->RPC("recieveDISARMEXPLOSIVE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendSPREADEFFECT(RPCParameters *rpcParameters) { server->RPC("recieveSPREADEFFECT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendNEWSMOKEEFFECT(RPCParameters *rpcParameters) { server->RPC("recieveNEWSMOKEEFFECT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendEXPLOSIONDAMAGE(RPCParameters *rpcParameters) { server->RPC("recieveEXPLOSIONDAMAGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendSTATE(RPCParameters *rpcParameters) { server->RPC("recieveSTATE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDEATH(RPCParameters *rpcParameters) { // the master copy of the scoreboard is kept on the server death_struct* nDeath = (death_struct*)rpcParameters->input; // Save Stats on the server side gMPPlayerStats[nDeath->soldier_team-1].deaths++; gMPPlayerStats[nDeath->attacker_team-1].kills++; // get the client number of the client sending the message int iCLnum = f_rec_num(3,rpcParameters->sender)+1; server->RPC("recieveDEATH",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); #ifdef JA2BETAVERSION wchar_t ateam[5]; wchar_t steam[5]; wchar_t clnum[5]; _itow(nDeath->attacker_team,ateam,10); _itow(nDeath->soldier_team,steam,10); _itow(iCLnum,clnum,10); ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"DEBUG: Soldier Killed : Attacking team %s , Soldier Team %s, Sender %s",ateam,steam,clnum); char logmsg[100]; sprintf(logmsg, "MP DEBUG: Soldier Killed #%i : Attacking team %i , Soldier Team %i, Sender %i\n",nDeath->soldier_id,nDeath->attacker_team,nDeath->soldier_team,iCLnum); MPDebugMsg( logmsg ); #endif } void sendhitSTRUCT(RPCParameters *rpcParameters) { EV_S_STRUCTUREHIT* miss = (EV_S_STRUCTUREHIT*)rpcParameters->input; SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; if (pAttacker != NULL) { int team = MercPtrs[ miss->ubAttackerID ]->bTeam; // AI if (team == 1) team = 4; // Clients else if (team >= 6) team -= 6; else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution Assert(team<5); // FIXME gMPPlayerStats[team].misses++; } server->RPC("recievehitSTRUCT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendhitWINDOW(RPCParameters *rpcParameters) { EV_S_WINDOWHIT* miss = (EV_S_WINDOWHIT*)rpcParameters->input; SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; if (pAttacker != NULL) { int team = MercPtrs[ miss->ubAttackerID ]->bTeam; // AI if (team == 1) team = 4; // Clients else if (team >= 6) team -= 6; else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution Assert(team<5); // FIXME gMPPlayerStats[team].misses++; } server->RPC("recievehitWINDOW",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendMISS(RPCParameters *rpcParameters) { EV_S_MISS* miss = (EV_S_MISS*)rpcParameters->input; SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; if (pAttacker != NULL) { int team = MercPtrs[ miss->ubAttackerID ]->bTeam; // AI if (team == 1) team = 4; // Clients else if (team >= 6) team -= 6; else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution Assert(team<5); // FIXME gMPPlayerStats[team].misses++; } server->RPC("recieveMISS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void updatenetworksoldier(RPCParameters *rpcParameters) { server->RPC("UpdateSoldierFromNetwork",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void Snull_team(RPCParameters *rpcParameters) { server->RPC("null_team",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendFIREW(RPCParameters *rpcParameters) { server->RPC("recieve_fireweapon",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendDOOR(RPCParameters *rpcParameters) { server->RPC("recieve_door",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void endINTERRUPT(RPCParameters *rpcParameters) { server->RPC("resume_turn",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendWIPE(RPCParameters *rpcParameters) { server->RPC("recieve_wipe",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendHEAL(RPCParameters *rpcParameters) { server->RPC("recieve_heal",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } // OJW - edge and team changes void sendEDGECHANGE(RPCParameters *rpcParameters) { server->RPC("recieveEDGECHANGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendTEAMCHANGE(RPCParameters *rpcParameters) { server->RPC("recieveTEAMCHANGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void requestSETID(SystemAddress addr) { server->RPC("requestSETID","", 0, HIGH_PRIORITY, RELIABLE, 0, addr, false, 0, UNASSIGNED_NETWORK_ID,0); } void receiveSETID(RPCParameters *rpcParameters) { setID = atoi((const char *)rpcParameters->input); // WANNE: FILE TRANSFER: Send the files to the client fltServer.Send(&fileList,server,rpcParameters->sender,setID,MEDIUM_PRIORITY,0,false, &incrementalReadInterface, 5000); } void startCOMBAT(RPCParameters *rpcParameters) { if(!( gTacticalStatus.uiFlags & INCOMBAT )) { gTacticalStatus.uiFlags |= INCOMBAT; sc_struct* data = (sc_struct*)rpcParameters->input; EndTurn( data->ubStartingTeam ); } } void sendREAL(RPCParameters *rpcParameters) { real_struct* rData = (real_struct*)rpcParameters->input; if(readyteamreg[rData->bteam]==0) { readyteamreg[rData->bteam]=1;//register vote, to prevent double voting ;p~ //hayden numreadyteams++; int numactiveteams=0; int b; for(int i=6;i<=LAST_TEAM;i++) { if(i==6) b=0; else b=i; if(gTacticalStatus.Team[ b ].bTeamActive) numactiveteams++; } //check # clients ready for realtime if (numreadyteams >= numactiveteams) { //if all send notification for realtime changeover //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"Switching to realtime..." ); numreadyteams=0; memset( &readyteamreg , 0 , sizeof (int) * 10); server->RPC("gotoRT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } } } // 20081222 - OJW void sendGAMEOVER(RPCParameters *rpcParameters) { // ignore the RPCParams and send the server side scoreboard server->RPC("recieveGAMEOVER",(const char*)gMPPlayerStats, sizeof(gMPPlayerStats)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } void sendCHATMSG(RPCParameters *rpcParameters) { // ignore the RPCParams and send the server side scoreboard server->RPC("recieveCHATMSG",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } // OJW - 20081223 // fix client disconnecting mid game, allowing the game to proceed void HandleDisconnect(SystemAddress sender) { // find the CLIENT_NUM of the player int x; client_data cl_record; for ( x=0; x<4;x++) { cl_record = client_d[x]; if(cl_record.address.binaryAddress==sender.binaryAddress && cl_record.address.port==sender.port) { // notify all the clients of the disconnect server->RPC("recieveDISCONNECT",(const char*)&cl_record.cl_number , sizeof(int)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); f_rec_num(2,sender); // remove from server's client list break; } } } // OJW - 20081218 // shuffle an integer array // remove shuffledList and put directly into arr[] void rSortArray(int* arr, int len) { std::list tmpList; std::list shuffledList; std::list::iterator Iter; int i=0; // add all our array items for(i=0; i0; iRandPos--, Iter++); shuffledList.push_front(*Iter); tmpList.erase(Iter); } // add all our elements for(i=0; isender;//get senders address filetransfersettings_struct fts; fts.syncClientsDirectory = gSyncGameDirectory; strcpy(fts.fileTransferDirectory, s_ServerId.getServerId(vfs::Path(gzFileTransferDirectory)).utf8().c_str()); strcpy(fts.serverName, cServerName); fts.totalTransferBytes = fileListTotalBytes; // OJW - 200907819 - Only send to the client that asked for it server->RPC("recieveFILE_TRANSFER_SETTINGS",(const char*)&fts, (int)sizeof(filetransfersettings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, sender, false, 0, UNASSIGNED_NETWORK_ID,0); } //************************* //UNASSIGNED_SYSTEM_ADDRESS //START INTERNAL SERVER //************************* //void send_settings (void)//send server settings to client void requestSETTINGS(RPCParameters *rpcParameters ) { // dont generate or send settings to a new user if they are about to be disconnected // because no more players can join the the game if (can_joingame()) { client_info* clinf = (client_info*)rpcParameters->input; // OJW - 20090507 // Disconnect if version is wrong if (strcmp(clinf->client_version,MPVERSION)!=0) { CHAR16 verErrMsg[255]; swprintf(verErrMsg,255, MPClientMessage[66], clinf->client_version,MPVERSION); // send disconnect reason only to this client server->RPC("recieveDISCONNECTREASON",(const char*)&verErrMsg, (int)sizeof(CHAR16)*255*8, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, false, 0, UNASSIGNED_NETWORK_ID,0); ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"CONNECTION REJECTED - CLIENT HAS WRONG VERSION"); // disconnect this client server->CloseConnection(rpcParameters->sender, true); return; } //server assigned client numbers - hayden. SystemAddress sender = rpcParameters->sender;//get senders address int bslot = f_rec_num(0,blank);//get empty record slot client_d[bslot].address=sender; //record clients address int new_cl_num = bslot+1;//client number to assign client_d[bslot].cl_number=new_cl_num; //record clients number settings_struct lan; lan.client_num = new_cl_num; //new server assigned number strcpy(lan.client_name , clinf->client_name); lan.randomStartingEdge = gRandomStartingEdge; lan.randomMercs = gRandomMercs; lan.maxClients = gMaxClients; memcpy(lan.kitBag , gKitBag,sizeof (char)*100); lan.damageMultiplier = gDamageMultiplier; lan.sameMercAllowed = gSameMercAllowed; lan.gsMercArriveSectorX = gsMercArriveSectorX; lan.gsMercArriveSectorY = gsMercArriveSectorY; lan.enemyEnabled = gEnemyEnabled; lan.creatureEnabled = gCreatureEnabled; lan.militiaEnabled = gMilitiaEnabled; lan.civEnabled = gCivEnabled; lan.gameType = gGameType; lan.disableMorale = gDisableMorale; lan.reportHiredMerc = gReportHiredMerc; lan.secondsPerTick = gSecondsPerTick; // WANNE.MP: Check lan.soubBobbyRayQuality = BR_AWESOME; lan.soubBobbyRayQuantity = BR_AWESOME; lan.sofGunNut = TRUE; lan.soubGameStyle = STYLE_REALISTIC; lan.soubDifficultyLevel = gDifficultyLevel; lan.soubSkillTraits = gSkillTraits; lan.sofTurnTimeLimit = TRUE; lan.sofIronManMode = FALSE; lan.startingCash = gStartingCash; // Old/Old if (gInventoryAttachment == INVENTORY_OLD) { lan.inventoryAttachment = 0; } else { // New/Old if (gGameOptions.ubAttachmentSystem == ATTACHMENT_OLD) { lan.inventoryAttachment = 1; // New/Old } // New/New else { lan.inventoryAttachment = 2; // New/New } } lan.disableBobbyRay=gDisableBobbyRay; lan.disableMercEquipment=gDisableMercEquipment; lan.maxMercs = gMaxMercs; memcpy( lan.client_names , client_names, sizeof( char ) * 4 * 30 ); lan.team=clinf->team; // OJW - 20090530 - fix teams not initialised properly client_teams[ lan.client_num - 1 ] = lan.team; // OJW - 20081218 if (gRandomStartingEdge) { // Get the edge from the randomized "client_edges" lan.startingSectorEdge = client_edges[lan.client_num-1]; } else { // WANNE: on DM, each client should get a unique starting edge per default if (gGameType == MP_TYPE_DEATHMATCH || gGameType == MP_TYPE_TEAMDEATMATCH) { client_edges[0] = MP_EDGE_NORTH; // client 1 client_edges[1] = MP_EDGE_SOUTH; // client 2 client_edges[2] = MP_EDGE_EAST; // client 3 client_edges[3] = MP_EDGE_WEST; // client 4 lan.startingSectorEdge = client_edges[lan.client_num-1]; } else { lan.startingSectorEdge=clinf->cl_edge; } } // OJW - 20081223 if (gRandomMercs) { mpTeams.SerializeProfiles(lan.random_mercs); } lan.startingTime = gStartingTime; lan.weaponReadyBonus = gWeaponReadyBonus; lan.inventoryAttachment = gInventoryAttachment; lan.disableSpectatorMode = gDisableSpectatorMode; // OJW - 20081204 strcpy(lan.server_name , cServerName); memcpy(lan.client_edges,client_edges,sizeof(int)*5); memcpy(lan.client_teams,client_teams,sizeof(int)*4); // OJW - 20091024 - send servers random table memcpy(lan.random_table,guiPreRandomNums,sizeof(UINT32)*MAX_PREGENERATED_NUMS); // OJW - 20090507 // send server version to client strcpy(lan.server_version,MPVERSION); server->RPC("recieveSETTINGS",(const char*)&lan, (int)sizeof(settings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); // WANNE: FILE TRANSFER: A client connected -> start the file transfer! if (gSyncGameDirectory) requestSETID(rpcParameters->sender); } } // added 081201 by Owen , allow the server to change the map while its still not in laptop mode void send_mapchange(void) { if (is_server && !allowlaptop) { mapchange_struct lan; lan.gsMercArriveSectorX=gsMercArriveSectorX; lan.gsMercArriveSectorY=gsMercArriveSectorY; lan.startingTime = gStartingTime; server->RPC("recieveMAPCHANGE",(const char*)&lan, (int)sizeof(mapchange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } else { ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[45]); } } // OJW 090212 bool inline can_joingame() { return !allowlaptop; } // Allow server to disconnect incoming clients after the game has started void CheckIncomingConnection(Packet* p) { // some clients might reconnect after disconnecting, either after the laptop is unlocked // or after the game has started // we dont want to allow this as thier game will be out of state if (!can_joingame()) { ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"CONNECTION REJECTED - GAME HAS STARTED"); // disconnect this client, no need to notify them as they will know if they disconnected // before receiving a settings packet that they were not allowed to join server->CloseConnection(p->systemAddress, true); } } void AddFilesToSendList() { // we cannot just iterate over "*/*" as we would get ALL files and we don't want to send all files // instead we only iterate over the files in the "_MULTIPLAYER" profile vfs::CProfileStack *PS = getVFS()->getProfileStack(); vfs::CVirtualProfile *prof = PS->getProfile("_MULTIPLAYER"); if(prof != PS->topProfile()) { // there is not supposed to be another profile? // output error message return; } CTransferRules transferRules; transferRules.initFromTxtFile("transfer_rules.txt"); vfs::IBaseLocation* loc = prof->getLocation(""); SGP_THROW_IFFALSE(loc != NULL, "MP profile was successfully created, but the root directory is not included"); vfs::IBaseLocation::Iterator it = loc->begin(); int i=0; for(; !it.end(); it.next(), i++) { vfs::Path const& valid_path = it.value()->getPath(); if(transferRules.applyRule(valid_path()) == CTransferRules::ACCEPT) { // transfer only those files that are not on the ignore list vfs::tReadableFile* rfile = vfs::tReadableFile::cast(it.value()); if(!rfile) { continue; } vfs::size_t fsize = rfile->getSize(); fileListTotalBytes += (long)fsize; if( (fsize>0) && rfile->openRead()) { std::vector data(fsize,0); rfile->read(&data[0], fsize); rfile->close(); fileList.AddFile(vfs::String::as_utf8(valid_path()).c_str(),&data[0], fsize,fsize,FileListNodeContext(0,0), false); } } } } void start_server (void) { if(!is_server) { f_rec_num(1,blank);//wipe clean // ---------------------------- // Read from ja2_mp.ini // ---------------------------- CIniReader iniReader(JA2MP_INI_FILENAME); // Wird nur für Strings gebraucht strncpy(cServerName, iniReader.ReadString(JA2MP_INI_INITIAL_SECTION, JA2MP_SERVER_NAME, "My JA2 Server"), 30 ); strncpy(gKitBag, iniReader.ReadString(JA2MP_INI_INITIAL_SECTION,JA2MP_KIT_BAG, ""), 100); vfs::PropertyContainer props; props.initFromIniFile(JA2MP_INI_FILENAME); UINT16 serverPort = (UINT16)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_SERVER_PORT, 60005); UINT8 maxClients = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_MAX_CLIENTS, 4); UINT8 sameMercAllowed = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_SAME_MERC, 1); UINT8 civEnabled = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_CIV_ENABLED, 0); UINT8 gameType = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_GAME_MODE, 0); UINT8 difficultyLevel = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_DIFFICULT_LEVEL, 3); UINT8 skillTraits = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_NEW_TRAITS, 0); UINT8 randomMercs = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION,JA2MP_RANDOM_MERCS, 0); UINT8 randomStartingEdge = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_RANDOM_EDGES, 0); UINT8 damageSelection = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_DAMAGE_MULTIPLIER, 1); UINT8 maxEnemiesEnabled = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_OVERRIDE_MAX_AI, 0); UINT8 syncGameDirectory = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_SYNC_CLIENTS_MP_DIR, 1); UINT8 reportHiredMerc = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_REPORT_NAME, 1); UINT8 startingCashSelection = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_STARTING_BALANCE, 25000); UINT8 timeTurnsSelection = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_TIMED_TURN_SECS_PER_TICK, 2); UINT8 disableBobbyRay = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_DISABLE_BOBBY_RAYS, 0); UINT8 maxMercs = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_MAX_MERCS, 6); UINT8 timeSelection = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_TIME, 1); UINT8 inventoryAttachment = (UINT8)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_ALLOW_CUSTOM_NIV, 0); // ---------------------------- // Save to global values // ---------------------------- gMaxClients = maxClients; gMaxEnemiesEnabled = 0; gDisableMorale = 0; gSyncGameDirectory = syncGameDirectory; gReportHiredMerc = reportHiredMerc; gDisableBobbyRay = disableBobbyRay; gDisableMercEquipment = 0; // Disable AIM and MERC equipment gMaxMercs = maxMercs; gGameType = gameType; gDifficultyLevel = difficultyLevel + 1; gSkillTraits = skillTraits; gEnemyEnabled = 0; gCreatureEnabled = 0; gMilitiaEnabled = 0; gCivEnabled = 0; gRandomMercs = randomMercs; gRandomStartingEdge = randomStartingEdge; gWeaponReadyBonus = 0; gDisableSpectatorMode = 0; gInventoryAttachment = inventoryAttachment; if (gRandomStartingEdge) { // create random starting edges int spawns[5] = { 0 , 1 , 2 , 3, 9 }; // 9 == Center // Randomize spawns rSortArray(spawns,5); memcpy(client_edges,spawns,sizeof(int)*5); } if (gRandomMercs) { // randomly sort team indexes to give client // one of four random merc teams rSortArray(client_mercteam,4); mpTeams.HandleServerStarted(); } if(gGameType == MP_TYPE_COOP)//only enable ai during coop { gEnemyEnabled = 1; // always enable enemies in co-op gMilitiaEnabled = 0; // always disable militia gCivEnabled = civEnabled; gMaxEnemiesEnabled = maxEnemiesEnabled; } // random_mercs implies same_merc gSameMercAllowed = gRandomMercs ? 1 : sameMercAllowed; switch (damageSelection) { case 0: // Very Low gDamageMultiplier = 0.2f; break; case 1: // Low gDamageMultiplier = 0.7f; break; case 2: // Normal gDamageMultiplier = 1.0f; break; } switch (timeTurnsSelection) { case 0: // Never gSecondsPerTick = 0; break; case 1: // Slow gSecondsPerTick = 5; break; case 2: // Medium gSecondsPerTick = 100; break; case 3: // Fast gSecondsPerTick = 400; break; } switch (startingCashSelection) { case 0: // Low gStartingCash = 5000; break; case 1: // Medium gStartingCash = 50000; break; case 2: // High gStartingCash = 100000; break; case 3: // Unlimited gStartingCash = 999999999; break; } switch (timeSelection) { case 0: // Morning gStartingTime = 7.00f; break; case 1: // Afternoon gStartingTime = 13.00f; break; case 2: // Night gStartingTime = 2.00; break; } //********************** ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[0] ); server=RakNetworkFactory::GetRakPeerInterface(); // WANNE: Set higher timeout than default (30 seconds) server->SetTimeoutTime(120000, UNASSIGNED_SYSTEM_ADDRESS); // 120 Seconds bool b = server->Startup(gMaxClients, 30, &SocketDescriptor(serverPort,0), 1); server->SetMaximumIncomingConnections((gMaxClients)); server->SetOccasionalPing(true); //RPC's REGISTER_STATIC_RPC(server, sendPATH); REGISTER_STATIC_RPC(server, sendDOWNLOADSTATUS); REGISTER_STATIC_RPC(server, sendSTANCE); REGISTER_STATIC_RPC(server, sendDIR); REGISTER_STATIC_RPC(server, sendFIRE); REGISTER_STATIC_RPC(server, sendHIT); REGISTER_STATIC_RPC(server, sendHIRE); REGISTER_STATIC_RPC(server, sendDISMISS); REGISTER_STATIC_RPC(server, sendguiPOS); REGISTER_STATIC_RPC(server, sendguiDIR); REGISTER_STATIC_RPC(server, sendEndTurn); REGISTER_STATIC_RPC(server, sendAI); REGISTER_STATIC_RPC(server, sendSTOP); REGISTER_STATIC_RPC(server, sendINTERRUPT); REGISTER_STATIC_RPC(server, sendREADY); REGISTER_STATIC_RPC(server, sendGUI); REGISTER_STATIC_RPC(server, sendBULLET); REGISTER_STATIC_RPC(server, sendGRENADE); REGISTER_STATIC_RPC(server, sendGRENADERESULT); REGISTER_STATIC_RPC(server, sendPLANTEXPLOSIVE); REGISTER_STATIC_RPC(server, sendDETONATEEXPLOSIVE); REGISTER_STATIC_RPC(server, sendDISARMEXPLOSIVE); REGISTER_STATIC_RPC(server, sendSPREADEFFECT); REGISTER_STATIC_RPC(server, sendNEWSMOKEEFFECT); REGISTER_STATIC_RPC(server, sendEXPLOSIONDAMAGE); REGISTER_STATIC_RPC(server, requestSETTINGS); REGISTER_STATIC_RPC(server, requestFILE_TRANSFER_SETTINGS); REGISTER_STATIC_RPC(server, sendSTATE); REGISTER_STATIC_RPC(server, sendDEATH); REGISTER_STATIC_RPC(server, sendhitSTRUCT); REGISTER_STATIC_RPC(server, sendhitWINDOW); REGISTER_STATIC_RPC(server, sendMISS); REGISTER_STATIC_RPC(server, updatenetworksoldier); REGISTER_STATIC_RPC(server, Snull_team); REGISTER_STATIC_RPC(server, sendFIREW); REGISTER_STATIC_RPC(server, sendDOOR); REGISTER_STATIC_RPC(server, endINTERRUPT); REGISTER_STATIC_RPC(server, sendREAL); REGISTER_STATIC_RPC(server, startCOMBAT); REGISTER_STATIC_RPC(server, sendWIPE); REGISTER_STATIC_RPC(server, sendHEAL); REGISTER_STATIC_RPC(server, sendEDGECHANGE); REGISTER_STATIC_RPC(server, sendTEAMCHANGE); REGISTER_STATIC_RPC(server, sendGAMEOVER); REGISTER_STATIC_RPC(server, sendCHATMSG); REGISTER_STATIC_RPC(server, receiveSETID); if (b) { ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[1]); is_server = true; // WANNE: FILE TRANSFER server->AttachPlugin(&fltServer); server->SetSplitMessageProgressInterval(1); fltServer.SetCallback(&serverFileListProgress); fileListTotalBytes=0; if (gSyncGameDirectory == 1) { AddFilesToSendList(); } connect_client();//connect client to server } else { ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[4]); } } else { ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[3]); } } // recieve and process server info packets void server_packet ( void ) { Packet* p; if (is_server) { p = server->Receive(); while(p) { //continue; // Didn't get any packets // We got a packet, get the identifier with our handy function SpacketIdentifier = SGetPacketIdentifier(p); //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"packet recieved"); // Check if this is a network message packet switch (SpacketIdentifier) { case ID_DISCONNECTION_NOTIFICATION://client disconnected purposefullly // Connection lost normally ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_DISCONNECTION_NOTIFICATION"); HandleDisconnect(p->systemAddress);//clear record break; case ID_ALREADY_CONNECTED: // Connection lost normally ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_ALREADY_CONNECTED"); break; case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); break; case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOTE_CONNECTION_LOST"); break; case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOT/MING_CONNECTION"); break; case ID_CONNECTION_ATTEMPT_FAILED: ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_ATTEMPT_FAILED"); break; case ID_NO_FREE_INCOMING_CONNECTIONS: // Sorry, the server is full. I don't do anything here but // A real app should tell the user ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_NO_FREE_INCOMING_CONNECTIONS"); break; case ID_CONNECTION_LOST: // Couldn't deliver a reliable packet - i.e. the other system was abnormally // terminated ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_LOST");//client dropped HandleDisconnect(p->systemAddress);//clear record break; case ID_CONNECTION_REQUEST_ACCEPTED: // This tells the client they have connected ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_REQUEST_ACCEPTED"); break; case ID_NEW_INCOMING_CONNECTION: //tells server client has connected #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_NEW_INCOMING_CONNECTION"); #endif // make sure they can connect CheckIncomingConnection(p); //send_settings();//send off server set settings break; case ID_MODIFIED_PACKET: // Cheater! #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_MODIFIED_PACKET"); #endif break; default: #ifdef JA2BETAVERSION ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"** a packet has been recieved for which i dont know what to do... **"); #endif break; } // We're done with the packet, get more :) server->DeallocatePacket(p); p = server->Receive(); } } } // Copied from Multiplayer.cpp // If the first byte is ID_TIMESTAMP, then we want the 5th byte // Otherwise we want the 1st byte unsigned char SGetPacketIdentifier(Packet *p) { if (p==0) return 255; if ((unsigned char)p->data[0] == ID_TIMESTAMP) { assert(p->length > sizeof(unsigned char) + sizeof(unsigned long)); return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)]; } else return (unsigned char) p->data[0]; } void server_disconnect (void) { if(is_server) { server->DetachPlugin(&fltServer); server->Shutdown(300); is_server = false; fileList.Clear(); // We're done with the network RakNetworkFactory::DestroyRakPeerInterface(server); ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[6]); } else { ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[7]); } }