2016-03-12 01:20:51 +01:00
|
|
|
/* Copyright (c) 2002-2012 Croteam Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of version 2 of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation
|
|
|
|
|
|
|
|
|
|
|
|
This program 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. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
|
2016-03-11 14:57:17 +01:00
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#include <Engine/StdH.h>
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
#include <Engine/Base/Console.h>
|
|
|
|
#include <Engine/Network/Network.h>
|
|
|
|
#include <Engine/Network/PlayerSource.h>
|
|
|
|
#include <Engine/Network/PlayerTarget.h>
|
|
|
|
#include <Engine/Network/CommunicationInterface.h>
|
|
|
|
#include <Engine/Network/SessionState.h>
|
|
|
|
#include <Engine/Templates/StaticArray.cpp>
|
|
|
|
#include <Engine/Entities/InternalClasses.h>
|
|
|
|
|
|
|
|
extern FLOAT net_tmConnectionTimeout;
|
|
|
|
extern FLOAT net_tmProblemsTimeOut;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constructor.
|
|
|
|
*/
|
|
|
|
CPlayerSource::CPlayerSource(void) {
|
|
|
|
pls_Active = FALSE;
|
|
|
|
pls_Index = -2;
|
|
|
|
pls_csAction.cs_iIndex = -1;
|
|
|
|
// clear action packet
|
|
|
|
pls_paAction.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destructor.
|
|
|
|
*/
|
|
|
|
CPlayerSource::~CPlayerSource(void) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Activate a new player.
|
|
|
|
*/
|
|
|
|
void CPlayerSource::Start_t(CPlayerCharacter &pcCharacter) // throw char *
|
|
|
|
{
|
|
|
|
ASSERT(!pls_Active);
|
|
|
|
|
|
|
|
// set index to -1 what means that you are not yet registered properly
|
|
|
|
pls_Index = -1;
|
|
|
|
// copy the character data
|
|
|
|
pls_pcCharacter = pcCharacter;
|
|
|
|
// clear actions
|
|
|
|
pls_paAction.Clear();;
|
|
|
|
for(INDEX ipa=0; ipa<PLS_MAXLASTACTIONS; ipa++) {
|
|
|
|
pls_apaLastActions[ipa].Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// request player connection
|
|
|
|
CNetworkMessage nmRegisterPlayer(MSG_REQ_CONNECTPLAYER);
|
|
|
|
nmRegisterPlayer<<pls_pcCharacter; // player's character data
|
|
|
|
_pNetwork->SendToServerReliable(nmRegisterPlayer);
|
|
|
|
|
|
|
|
for(TIME tmWait=0;
|
|
|
|
tmWait<net_tmConnectionTimeout*1000;
|
2016-03-29 03:03:54 +02:00
|
|
|
_pTimer->Sleep(NET_WAITMESSAGE_DELAY), tmWait+=NET_WAITMESSAGE_DELAY) {
|
2016-03-11 14:57:17 +01:00
|
|
|
if (_pNetwork->ga_IsServer) {
|
|
|
|
_pNetwork->TimerLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_cmiComm.Client_Update() == FALSE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// wait for message to come
|
|
|
|
CNetworkMessage nmReceived;
|
|
|
|
if (!_pNetwork->ReceiveFromServerReliable(nmReceived)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is the init message
|
|
|
|
if (nmReceived.GetType() == MSG_REP_CONNECTPLAYER) {
|
|
|
|
// remember your index
|
|
|
|
nmReceived>>pls_Index;
|
|
|
|
// finish waiting
|
|
|
|
pls_Active = TRUE;
|
|
|
|
return;
|
|
|
|
// if this is disconnect message
|
|
|
|
} else if (nmReceived.GetType() == MSG_INF_DISCONNECTED) {
|
|
|
|
// confirm disconnect
|
|
|
|
CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
|
|
|
|
_pNetwork->SendToServerReliable(nmConfirmDisconnect);
|
|
|
|
|
|
|
|
// throw exception
|
|
|
|
CTString strReason;
|
|
|
|
nmReceived>>strReason;
|
|
|
|
_pNetwork->ga_sesSessionState.ses_strDisconnected = strReason;
|
2016-03-29 03:03:54 +02:00
|
|
|
ThrowF_t(TRANS("Cannot add player because: %s\n"), (const char *) strReason);
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// otherwise
|
|
|
|
} else {
|
|
|
|
// it is invalid message
|
|
|
|
ThrowF_t(TRANS("Invalid message while waiting for player registration"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// if client is disconnected
|
|
|
|
if (!_cmiComm.Client_IsConnected()) {
|
|
|
|
// quit
|
|
|
|
ThrowF_t(TRANS("Client disconnected"));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
CNetworkMessage nmConfirmDisconnect(MSG_REP_DISCONNECTED);
|
|
|
|
_pNetwork->SendToServerReliable(nmConfirmDisconnect);
|
|
|
|
|
|
|
|
ThrowF_t(TRANS("Timeout while waiting for player registration"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deactivate removed player.
|
|
|
|
*/
|
|
|
|
void CPlayerSource::Stop(void)
|
|
|
|
{
|
|
|
|
ASSERT(pls_Active);
|
|
|
|
pls_Active = FALSE;
|
|
|
|
pls_Index = -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// request character change for a player
|
|
|
|
// NOTE: the request is asynchronious and possible failure cannot be detected
|
|
|
|
void CPlayerSource::ChangeCharacter(CPlayerCharacter &pcNew)
|
|
|
|
{
|
|
|
|
// if the requested character has different guid
|
|
|
|
if (!(pls_pcCharacter==pcNew)) {
|
|
|
|
// fail
|
2016-03-29 05:21:44 +02:00
|
|
|
CPrintF(TRANSV("Cannot update character - different GUID\n"));
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// just request the change
|
|
|
|
CNetworkMessage nmChangeChar(MSG_REQ_CHARACTERCHANGE);
|
|
|
|
nmChangeChar<<pls_Index<<pcNew;
|
|
|
|
_pNetwork->SendToServerReliable(nmChangeChar);
|
|
|
|
// remember new setting
|
|
|
|
pls_pcCharacter = pcNew;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set player action.
|
|
|
|
*/
|
|
|
|
void CPlayerSource::SetAction(const CPlayerAction &paAction)
|
|
|
|
{
|
|
|
|
// synchronize access to action
|
|
|
|
CTSingleLock slAction(&pls_csAction, TRUE);
|
|
|
|
// set action
|
|
|
|
pls_paAction = paAction;
|
|
|
|
pls_paAction.pa_llCreated = _pTimer->GetHighPrecisionTimer().GetMilliseconds();
|
|
|
|
//CPrintF("%.2f - created: %d\n", _pTimer->GetRealTimeTick(), SLONG(pls_paAction.pa_llCreated));
|
|
|
|
}
|
|
|
|
|
|
|
|
// get mask of this player for chat messages
|
|
|
|
ULONG CPlayerSource::GetChatMask(void)
|
|
|
|
{
|
|
|
|
return 1UL<<pls_Index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create action packet from current player commands and for sending to server. */
|
|
|
|
void CPlayerSource::WriteActionPacket(CNetworkMessage &nm)
|
|
|
|
{
|
|
|
|
// synchronize access to actions
|
|
|
|
CTSingleLock slActions(&pls_csAction, TRUE);
|
|
|
|
|
|
|
|
// check if active and registered
|
|
|
|
CPlayerEntity *ppe = NULL;
|
|
|
|
if (IsActive()) {
|
|
|
|
ppe = (CPlayerEntity *)_pNetwork->GetLocalPlayerEntity(this);
|
|
|
|
}
|
|
|
|
// if not
|
|
|
|
if (ppe==NULL) {
|
|
|
|
// just write a dummy bit
|
|
|
|
BOOL bActive = 0;
|
|
|
|
nm.WriteBits(&bActive, 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// normalize action (remove invalid floats like -0)
|
|
|
|
pls_paAction.Normalize();
|
|
|
|
|
|
|
|
ASSERT(pls_Active);
|
|
|
|
ASSERT(pls_Index>=0);
|
|
|
|
|
|
|
|
// determine ping
|
|
|
|
FLOAT tmPing = ppe->en_tmPing;
|
|
|
|
INDEX iPing = (INDEX)ceil(tmPing*1000);
|
|
|
|
|
|
|
|
// write all in the message
|
|
|
|
BOOL bActive = 1;
|
|
|
|
nm.WriteBits(&bActive, 1);
|
|
|
|
nm.WriteBits(&pls_Index, 4); // your index
|
|
|
|
nm.WriteBits(&iPing, 10); // your ping
|
|
|
|
nm<<pls_paAction; // action
|
|
|
|
//CPrintF("%.2f - written: %d\n", _pTimer->GetRealTimeTick(), SLONG(pls_paAction.pa_llCreated));
|
|
|
|
|
|
|
|
// get sendbehind parameters
|
|
|
|
extern INDEX cli_iSendBehind;
|
|
|
|
extern INDEX cli_bPredictIfServer;
|
|
|
|
cli_iSendBehind = Clamp(cli_iSendBehind, 0L, 3L);
|
|
|
|
INDEX iSendBehind = cli_iSendBehind;
|
|
|
|
|
|
|
|
// disable if server
|
|
|
|
if (_pNetwork->IsServer() && !cli_bPredictIfServer) {
|
|
|
|
iSendBehind = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save sendbehind if needed
|
|
|
|
nm.WriteBits(&iSendBehind, 2);
|
|
|
|
for(INDEX i=0; i<iSendBehind; i++) {
|
|
|
|
nm<<pls_apaLastActions[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// remember last action
|
|
|
|
for(INDEX ipa=1; ipa<PLS_MAXLASTACTIONS; ipa++) {
|
|
|
|
pls_apaLastActions[ipa] = pls_apaLastActions[ipa-1];
|
|
|
|
}
|
|
|
|
pls_apaLastActions[0] = pls_paAction;
|
|
|
|
|
|
|
|
// if not paused
|
|
|
|
if (!_pNetwork->IsPaused() && !_pNetwork->GetLocalPause()) {
|
|
|
|
// get the index of the player target in game state
|
|
|
|
INDEX iPlayerTarget = pls_Index;
|
|
|
|
// if player is added
|
|
|
|
if (iPlayerTarget>=0) {
|
|
|
|
// get the player target
|
|
|
|
CPlayerTarget &plt = _pNetwork->ga_sesSessionState.ses_apltPlayers[iPlayerTarget];
|
|
|
|
// let it buffer the packet
|
|
|
|
plt.PrebufferActionPacket(pls_paAction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|