529 lines
18 KiB
C++
529 lines
18 KiB
C++
/*
|
|
* Original work: Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
|
|
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
|
|
*
|
|
*
|
|
* Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt)
|
|
*
|
|
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
|
|
* license found in the license.txt file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "slikenetstuff.h"
|
|
|
|
#include "slikenet/NetworkIDManager.h"
|
|
#include "CDemo.h"
|
|
#include "slikenet/time.h"
|
|
#include "slikenet/GetTime.h"
|
|
#include "slikenet/SocketLayer.h"
|
|
#include "slikenet/linux_adapter.h"
|
|
#include "slikenet/osx_adapter.h"
|
|
|
|
using namespace SLNet;
|
|
|
|
RakPeerInterface *rakPeer;
|
|
NetworkIDManager *networkIDManager;
|
|
ReplicaManager3Irrlicht *irrlichtReplicaManager3;
|
|
NatPunchthroughClient *natPunchthroughClient;
|
|
CloudClient *cloudClient;
|
|
SLNet::FullyConnectedMesh2 *fullyConnectedMesh2;
|
|
PlayerReplica *playerReplica;
|
|
|
|
/*
|
|
class DebugBoxSceneNode : public scene::ISceneNode
|
|
{
|
|
public:
|
|
DebugBoxSceneNode(scene::ISceneNode* parent,
|
|
scene::ISceneManager* mgr,
|
|
s32 id = -1);
|
|
virtual const core::aabbox3d<f32>& getBoundingBox() const;
|
|
virtual void OnRegisterSceneNode();
|
|
virtual void render();
|
|
|
|
CDemo *demo;
|
|
};
|
|
DebugBoxSceneNode::DebugBoxSceneNode(
|
|
scene::ISceneNode* parent,
|
|
scene::ISceneManager* mgr,
|
|
s32 id)
|
|
: scene::ISceneNode(parent, mgr, id)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("DebugBoxSceneNode");
|
|
#endif
|
|
setAutomaticCulling(scene::EAC_OFF);
|
|
}
|
|
const core::aabbox3d<f32>& DebugBoxSceneNode::getBoundingBox() const
|
|
{
|
|
return demo->GetSyndeyBoundingBox();
|
|
}
|
|
void DebugBoxSceneNode::OnRegisterSceneNode()
|
|
{
|
|
if (IsVisible)
|
|
demo->GetSceneManager()->registerNodeForRendering(this, scene::ESNRP_SOLID);
|
|
}
|
|
void DebugBoxSceneNode::render()
|
|
{
|
|
if (DebugDataVisible)
|
|
{
|
|
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
|
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
|
|
|
video::SMaterial m;
|
|
m.Lighting = false;
|
|
demo->GetDevice()->getVideoDriver()->setMaterial(m);
|
|
demo->GetDevice()->getVideoDriver()->draw3DBox(demo->GetSyndeyBoundingBox());
|
|
}
|
|
}
|
|
*/
|
|
|
|
DataStructures::List<PlayerReplica*> PlayerReplica::playerList;
|
|
|
|
// Take this many milliseconds to move the visible position to the real position
|
|
static const float INTERP_TIME_MS=100.0f;
|
|
|
|
void InstantiateRakNetClasses(void)
|
|
{
|
|
static const int MAX_PLAYERS=32;
|
|
static const unsigned short TCP_PORT=0;
|
|
static const SLNet::TimeMS UDP_SLEEP_TIMER=30;
|
|
|
|
// Basis of all UDP communications
|
|
rakPeer= SLNet::RakPeerInterface::GetInstance();
|
|
// Using fixed port so we can use AdvertiseSystem and connect on the LAN if the server is not available.
|
|
SLNet::SocketDescriptor sd(1234,0);
|
|
sd.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255
|
|
while (IRNS2_Berkley::IsPortInUse(sd.port, sd.hostAddress, sd.socketFamily, SOCK_DGRAM)==true)
|
|
sd.port++;
|
|
// +1 is for the connection to the NAT punchthrough server
|
|
SLNET_VERIFY(rakPeer->Startup(MAX_PLAYERS+1,&sd,1) == SLNet::RAKNET_STARTED);
|
|
rakPeer->SetMaximumIncomingConnections(MAX_PLAYERS);
|
|
// Fast disconnect for easier testing of host migration
|
|
rakPeer->SetTimeoutTime(5000,UNASSIGNED_SYSTEM_ADDRESS);
|
|
// ReplicaManager3 replies on NetworkIDManager. It assigns numbers to objects so they can be looked up over the network
|
|
// It's a class in case you wanted to have multiple worlds, then you could have multiple instances of NetworkIDManager
|
|
networkIDManager=new NetworkIDManager;
|
|
// Automatically sends around new / deleted / changed game objects
|
|
irrlichtReplicaManager3=new ReplicaManager3Irrlicht;
|
|
irrlichtReplicaManager3->SetNetworkIDManager(networkIDManager);
|
|
rakPeer->AttachPlugin(irrlichtReplicaManager3);
|
|
// Automatically destroy connections, but don't create them so we have more control over when a system is considered ready to play
|
|
irrlichtReplicaManager3->SetAutoManageConnections(false,true);
|
|
// Create and register the network object that represents the player
|
|
playerReplica = new PlayerReplica;
|
|
irrlichtReplicaManager3->Reference(playerReplica);
|
|
// Lets you connect through routers
|
|
natPunchthroughClient=new NatPunchthroughClient;
|
|
rakPeer->AttachPlugin(natPunchthroughClient);
|
|
// Uploads game instance, basically client half of a directory server
|
|
// Server code is in NATCompleteServer sample
|
|
cloudClient=new CloudClient;
|
|
rakPeer->AttachPlugin(cloudClient);
|
|
fullyConnectedMesh2=new FullyConnectedMesh2;
|
|
fullyConnectedMesh2->SetAutoparticipateConnections(false);
|
|
fullyConnectedMesh2->SetConnectOnNewRemoteConnection(false, "");
|
|
rakPeer->AttachPlugin(fullyConnectedMesh2);
|
|
// Connect to the NAT punchthrough server
|
|
SLNET_VERIFY(rakPeer->Connect(DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_IP, DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_PORT,0,0) == CONNECTION_ATTEMPT_STARTED);
|
|
|
|
// Advertise ourselves on the lAN if the NAT punchthrough server is not available
|
|
//for (int i=0; i < 8; i++)
|
|
// rakPeer->AdvertiseSystem("255.255.255.255", 1234+i, 0,0,0);
|
|
}
|
|
void DeinitializeRakNetClasses(void)
|
|
{
|
|
// Shutdown so the server knows we stopped
|
|
rakPeer->Shutdown(100,0);
|
|
SLNet::RakPeerInterface::DestroyInstance(rakPeer);
|
|
delete networkIDManager;
|
|
delete irrlichtReplicaManager3;
|
|
delete natPunchthroughClient;
|
|
delete cloudClient;
|
|
delete fullyConnectedMesh2;
|
|
// ReplicaManager3 deletes all referenced objects, including this one
|
|
//playerReplica->PreDestruction(0);
|
|
//delete playerReplica;
|
|
}
|
|
BaseIrrlichtReplica::BaseIrrlichtReplica()
|
|
{
|
|
}
|
|
BaseIrrlichtReplica::~BaseIrrlichtReplica()
|
|
{
|
|
|
|
}
|
|
void BaseIrrlichtReplica::SerializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *destinationConnection)
|
|
{
|
|
// unused parameters
|
|
(void)destinationConnection;
|
|
|
|
constructionBitstream->Write(position);
|
|
}
|
|
bool BaseIrrlichtReplica::DeserializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *sourceConnection)
|
|
{
|
|
// unused parameters
|
|
(void)sourceConnection;
|
|
|
|
constructionBitstream->Read(position);
|
|
return true;
|
|
}
|
|
RM3SerializationResult BaseIrrlichtReplica::Serialize(SLNet::SerializeParameters *serializeParameters)
|
|
{
|
|
// unused parameters
|
|
(void)serializeParameters;
|
|
|
|
return RM3SR_BROADCAST_IDENTICALLY;
|
|
}
|
|
void BaseIrrlichtReplica::Deserialize(SLNet::DeserializeParameters *deserializeParameters)
|
|
{
|
|
// unused parameters
|
|
(void)deserializeParameters;
|
|
}
|
|
void BaseIrrlichtReplica::Update(SLNet::TimeMS curTime)
|
|
{
|
|
// unused parameters
|
|
(void)curTime;
|
|
}
|
|
PlayerReplica::PlayerReplica()
|
|
{
|
|
model=0;
|
|
rotationDeltaPerMS=0.0f;
|
|
isMoving=false;
|
|
deathTimeout=0;
|
|
lastUpdate= SLNet::GetTimeMS();
|
|
playerList.Push(this,_FILE_AND_LINE_);
|
|
}
|
|
PlayerReplica::~PlayerReplica()
|
|
{
|
|
unsigned int index = playerList.GetIndexOf(this);
|
|
if (index != (unsigned int) -1)
|
|
playerList.RemoveAtIndexFast(index);
|
|
}
|
|
void PlayerReplica::WriteAllocationID(SLNet::Connection_RM3 *destinationConnection, SLNet::BitStream *allocationIdBitstream) const
|
|
{
|
|
// unused parameters
|
|
(void)destinationConnection;
|
|
|
|
allocationIdBitstream->Write(SLNet::RakString("PlayerReplica"));
|
|
}
|
|
void PlayerReplica::SerializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *destinationConnection)
|
|
{
|
|
BaseIrrlichtReplica::SerializeConstruction(constructionBitstream, destinationConnection);
|
|
constructionBitstream->Write(rotationAroundYAxis);
|
|
constructionBitstream->Write(playerName);
|
|
constructionBitstream->Write(IsDead());
|
|
}
|
|
bool PlayerReplica::DeserializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *sourceConnection)
|
|
{
|
|
if (!BaseIrrlichtReplica::DeserializeConstruction(constructionBitstream, sourceConnection))
|
|
return false;
|
|
constructionBitstream->Read(rotationAroundYAxis);
|
|
constructionBitstream->Read(playerName);
|
|
constructionBitstream->Read(isDead);
|
|
return true;
|
|
}
|
|
void PlayerReplica::PostDeserializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *destinationConnection)
|
|
{
|
|
// unused parameters
|
|
(void)constructionBitstream;
|
|
(void)destinationConnection;
|
|
|
|
// Object was remotely created and all data loaded. Now we can make the object visible
|
|
scene::IAnimatedMesh* mesh = 0;
|
|
scene::ISceneManager *sm = demo->GetSceneManager();
|
|
mesh = sm->getMesh(IRRLICHT_MEDIA_PATH "sydney.md2");
|
|
model = sm->addAnimatedMeshSceneNode(mesh, 0);
|
|
|
|
// DebugBoxSceneNode * debugBox = new DebugBoxSceneNode(model,sm);
|
|
// debugBox->demo=demo;
|
|
// debugBox->setDebugDataVisible(true);
|
|
|
|
model->setPosition(position);
|
|
model->setRotation(core::vector3df(0, rotationAroundYAxis, 0));
|
|
model->setScale(core::vector3df(2,2,2));
|
|
model->setMD2Animation(scene::EMAT_STAND);
|
|
curAnim=scene::EMAT_STAND;
|
|
model->setMaterialTexture(0, demo->GetDevice()->getVideoDriver()->getTexture(IRRLICHT_MEDIA_PATH "sydney.bmp"));
|
|
model->setMaterialFlag(video::EMF_LIGHTING, true);
|
|
model->addShadowVolumeSceneNode();
|
|
model->setAutomaticCulling ( scene::EAC_BOX );
|
|
model->setVisible(true);
|
|
model->setAnimationEndCallback(this);
|
|
wchar_t playerNameWChar[1024];
|
|
mbstowcs_s(nullptr, playerNameWChar, playerName.C_String(), 1023);
|
|
scene::IBillboardSceneNode *bb = sm->addBillboardTextSceneNode(0, playerNameWChar, model);
|
|
bb->setSize(core::dimension2df(40,20));
|
|
bb->setPosition(core::vector3df(0,model->getBoundingBox().MaxEdge.Y+bb->getBoundingBox().MaxEdge.Y-bb->getBoundingBox().MinEdge.Y+5.0f,0));
|
|
bb->setColor(video::SColor(255,255,128,128), video::SColor(255,255,128,128));
|
|
}
|
|
void PlayerReplica::PreDestruction(SLNet::Connection_RM3 *sourceConnection)
|
|
{
|
|
// unused parameters
|
|
(void)sourceConnection;
|
|
|
|
if (model)
|
|
model->remove();
|
|
}
|
|
RM3SerializationResult PlayerReplica::Serialize(SLNet::SerializeParameters *serializeParameters)
|
|
{
|
|
BaseIrrlichtReplica::Serialize(serializeParameters);
|
|
serializeParameters->outputBitstream[0].Write(position);
|
|
serializeParameters->outputBitstream[0].Write(rotationAroundYAxis);
|
|
serializeParameters->outputBitstream[0].Write(isMoving);
|
|
serializeParameters->outputBitstream[0].Write(IsDead());
|
|
return RM3SR_BROADCAST_IDENTICALLY;
|
|
}
|
|
void PlayerReplica::Deserialize(SLNet::DeserializeParameters *deserializeParameters)
|
|
{
|
|
BaseIrrlichtReplica::Deserialize(deserializeParameters);
|
|
deserializeParameters->serializationBitstream[0].Read(position);
|
|
deserializeParameters->serializationBitstream[0].Read(rotationAroundYAxis);
|
|
deserializeParameters->serializationBitstream[0].Read(isMoving);
|
|
bool wasDead=isDead;
|
|
deserializeParameters->serializationBitstream[0].Read(isDead);
|
|
if (isDead==true && wasDead==false)
|
|
{
|
|
demo->PlayDeathSound(position);
|
|
}
|
|
|
|
core::vector3df positionOffset;
|
|
positionOffset=position-model->getPosition();
|
|
positionDeltaPerMS = positionOffset / INTERP_TIME_MS;
|
|
float rotationOffset;
|
|
rotationOffset=GetRotationDifference(rotationAroundYAxis,model->getRotation().Y);
|
|
rotationDeltaPerMS = rotationOffset / INTERP_TIME_MS;
|
|
interpEndTime = SLNet::GetTimeMS() + (SLNet::TimeMS) INTERP_TIME_MS;
|
|
}
|
|
void PlayerReplica::Update(SLNet::TimeMS curTime)
|
|
{
|
|
// Is a locally created object?
|
|
if (creatingSystemGUID==rakPeer->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS))
|
|
{
|
|
// Local player has no mesh to interpolate
|
|
// Input our camera position as our player position
|
|
playerReplica->position=demo->GetSceneManager()->getActiveCamera()->getPosition()-irr::core::vector3df(0,CAMERA_HEIGHT,0);
|
|
playerReplica->rotationAroundYAxis=demo->GetSceneManager()->getActiveCamera()->getRotation().Y-90.0f;
|
|
isMoving=demo->IsMovementKeyDown();
|
|
|
|
// Ack, makes the screen messed up and the mouse move off the window
|
|
// Find another way to keep the dead player from moving
|
|
// demo->EnableInput(IsDead()==false);
|
|
|
|
return;
|
|
}
|
|
|
|
// Update interpolation
|
|
SLNet::TimeMS elapsed = curTime-lastUpdate;
|
|
if (elapsed<=1)
|
|
return;
|
|
if (elapsed>100)
|
|
elapsed=100;
|
|
|
|
lastUpdate=curTime;
|
|
irr::core::vector3df curPositionDelta = position-model->getPosition();
|
|
irr::core::vector3df interpThisTick = positionDeltaPerMS*(float) elapsed;
|
|
if (curTime < interpEndTime && interpThisTick.getLengthSQ() < curPositionDelta.getLengthSQ())
|
|
{
|
|
model->setPosition(model->getPosition()+positionDeltaPerMS*(float) elapsed);
|
|
}
|
|
else
|
|
{
|
|
model->setPosition(position);
|
|
}
|
|
|
|
float curRotationDelta = GetRotationDifference(rotationAroundYAxis,model->getRotation().Y);
|
|
float interpThisTickRotation = rotationDeltaPerMS*(float)elapsed;
|
|
if (curTime < interpEndTime && fabs(interpThisTickRotation) < fabs(curRotationDelta))
|
|
{
|
|
model->setRotation(model->getRotation()+core::vector3df(0,interpThisTickRotation,0));
|
|
}
|
|
else
|
|
{
|
|
model->setRotation(core::vector3df(0,rotationAroundYAxis,0));
|
|
}
|
|
|
|
if (isDead)
|
|
{
|
|
UpdateAnimation(scene::EMAT_DEATH_FALLBACK);
|
|
model->setLoopMode(false);
|
|
}
|
|
else if (curAnim!=scene::EMAT_ATTACK)
|
|
{
|
|
if (isMoving)
|
|
{
|
|
UpdateAnimation(scene::EMAT_RUN);
|
|
model->setLoopMode(true);
|
|
}
|
|
else
|
|
{
|
|
UpdateAnimation(scene::EMAT_STAND);
|
|
model->setLoopMode(true);
|
|
}
|
|
}
|
|
}
|
|
void PlayerReplica::UpdateAnimation(irr::scene::EMD2_ANIMATION_TYPE anim)
|
|
{
|
|
if (anim!=curAnim)
|
|
model->setMD2Animation(anim);
|
|
curAnim=anim;
|
|
}
|
|
float PlayerReplica::GetRotationDifference(float r1, float r2)
|
|
{
|
|
float diff = r1-r2;
|
|
while (diff>180.0f)
|
|
diff-=360.0f;
|
|
while (diff<-180.0f)
|
|
diff+=360.0f;
|
|
return diff;
|
|
}
|
|
void PlayerReplica::OnAnimationEnd(scene::IAnimatedMeshSceneNode* node)
|
|
{
|
|
// unused parameters
|
|
(void)node;
|
|
|
|
if (curAnim==scene::EMAT_ATTACK)
|
|
{
|
|
if (isMoving)
|
|
{
|
|
UpdateAnimation(scene::EMAT_RUN);
|
|
model->setLoopMode(true);
|
|
}
|
|
else
|
|
{
|
|
UpdateAnimation(scene::EMAT_STAND);
|
|
model->setLoopMode(true);
|
|
}
|
|
}
|
|
}
|
|
void PlayerReplica::PlayAttackAnimation(void)
|
|
{
|
|
if (isDead==false)
|
|
{
|
|
UpdateAnimation(scene::EMAT_ATTACK);
|
|
model->setLoopMode(false);
|
|
}
|
|
}
|
|
bool PlayerReplica::IsDead(void) const
|
|
{
|
|
return deathTimeout > SLNet::GetTimeMS();
|
|
}
|
|
BallReplica::BallReplica()
|
|
{
|
|
creationTime= SLNet::GetTimeMS();
|
|
}
|
|
BallReplica::~BallReplica()
|
|
{
|
|
}
|
|
void BallReplica::WriteAllocationID(SLNet::Connection_RM3 *destinationConnection, SLNet::BitStream *allocationIdBitstream) const
|
|
{
|
|
// unused parameters
|
|
(void)destinationConnection;
|
|
|
|
allocationIdBitstream->Write(SLNet::RakString("BallReplica"));
|
|
}
|
|
void BallReplica::SerializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *destinationConnection)
|
|
{
|
|
BaseIrrlichtReplica::SerializeConstruction(constructionBitstream, destinationConnection);
|
|
constructionBitstream->Write(shotDirection);
|
|
}
|
|
bool BallReplica::DeserializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *sourceConnection)
|
|
{
|
|
if (!BaseIrrlichtReplica::DeserializeConstruction(constructionBitstream, sourceConnection))
|
|
return false;
|
|
constructionBitstream->Read(shotDirection);
|
|
return true;
|
|
}
|
|
void BallReplica::PostDeserializeConstruction(SLNet::BitStream *constructionBitstream, SLNet::Connection_RM3 *destinationConnection)
|
|
{
|
|
// unused parameters
|
|
(void)constructionBitstream;
|
|
(void)destinationConnection;
|
|
|
|
// Shot visible effect and BallReplica classes are not linked, but they update the same way, such that
|
|
// they are in the same spot all the time
|
|
demo->shootFromOrigin(position, shotDirection);
|
|
|
|
// Find the owner of this ball, and make them play the attack animation
|
|
unsigned int idx;
|
|
for (idx=0; idx < PlayerReplica::playerList.Size(); idx++)
|
|
{
|
|
if (PlayerReplica::playerList[idx]->creatingSystemGUID==creatingSystemGUID)
|
|
{
|
|
PlayerReplica::playerList[idx]->PlayAttackAnimation();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void BallReplica::PreDestruction(SLNet::Connection_RM3 *sourceConnection)
|
|
{
|
|
// unused parameters
|
|
(void)sourceConnection;
|
|
|
|
// The system that shot this ball destroyed it, or disconnected
|
|
// Technically we should clear out the node visible effect too, but it's not important for now
|
|
}
|
|
RM3SerializationResult BallReplica::Serialize(SLNet::SerializeParameters *serializeParameters)
|
|
{
|
|
BaseIrrlichtReplica::Serialize(serializeParameters);
|
|
return RM3SR_BROADCAST_IDENTICALLY;
|
|
}
|
|
void BallReplica::Deserialize(SLNet::DeserializeParameters *deserializeParameters)
|
|
{
|
|
BaseIrrlichtReplica::Deserialize(deserializeParameters);
|
|
}
|
|
void BallReplica::Update(SLNet::TimeMS curTime)
|
|
{
|
|
// Is a locally created object?
|
|
if (creatingSystemGUID==rakPeer->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS))
|
|
{
|
|
// Destroy if shot expired
|
|
if (curTime > shotLifetime)
|
|
{
|
|
// Destroy on network
|
|
BroadcastDestruction();
|
|
delete this;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Keep at the same position as the visible effect
|
|
// Deterministic, so no need to actually transmit position
|
|
// The variable position is the origin that the ball was created at. For the player, it is their actual position
|
|
SLNet::TimeMS elapsedTime;
|
|
// Due to ping variances and timestamp miscalculations, it's possible with very low pings to get a slightly negative time, so we have to check
|
|
if (curTime>=creationTime)
|
|
elapsedTime = curTime - creationTime;
|
|
else
|
|
elapsedTime=0;
|
|
irr::core::vector3df updatedPosition = position + shotDirection * (float) elapsedTime * SHOT_SPEED;
|
|
|
|
// See if the bullet hit us
|
|
if (creatingSystemGUID!=rakPeer->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS))
|
|
{
|
|
if (playerReplica->IsDead()==false)
|
|
{
|
|
//float playerHalfHeight=demo->GetSyndeyBoundingBox().getExtent().Y/2;
|
|
irr::core::vector3df positionRelativeToCharacter = updatedPosition-playerReplica->position;//+core::vector3df(0,playerHalfHeight,0);
|
|
if (demo->GetSyndeyBoundingBox().isPointInside(positionRelativeToCharacter))
|
|
//if ((playerReplica->position+core::vector3df(0,playerHalfHeight,0)-updatedPosition).getLengthSQ() < BALL_DIAMETER*BALL_DIAMETER/4.0f)
|
|
{
|
|
// We're dead for 3 seconds
|
|
playerReplica->deathTimeout=curTime+3000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SLNet::Replica3 *Connection_RM3Irrlicht::AllocReplica(SLNet::BitStream *allocationId, ReplicaManager3 *replicaManager3)
|
|
{
|
|
// unused parameters
|
|
(void)replicaManager3;
|
|
|
|
SLNet::RakString typeName; allocationId->Read(typeName);
|
|
if (typeName=="PlayerReplica") {BaseIrrlichtReplica *r = new PlayerReplica; r->demo=demo; return r;}
|
|
if (typeName=="BallReplica") {BaseIrrlichtReplica *r = new BallReplica; r->demo=demo; return r;}
|
|
return 0;
|
|
}
|