/* * psserverdr.cpp by Matze Braun * * Copyright (C) 2002 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include "client.h" #include "clients.h" #include "net/message.h" #include "net/msghandler.h" #include "engine/celbase.h" #include "engine/drmessage.h" #include "engine/netpersist.h" #include #include #include "psserverdr.h" #include "tutorialmanager.h" #include "events.h" #include "psserver.h" #include "cachemanager.h" #include "globals.h" #include "playergroup.h" #include "gem.h" #include "entitymanager.h" #include "paladinjr.h" #include "util/serverconsole.h" #include "util/log.h" #include "util/psconst.h" #include "util/mathscript.h" #include "psproxlist.h" #include "util/eventmanager.h" psServerDR::psServerDR() { entitymanager = NULL; clients = NULL; paladin = NULL; } psServerDR::~psServerDR() { if (psserver->GetEventManager()) psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_DEAD_RECKONING); delete paladin; } bool psServerDR::Initialize(EntityManager* entitymanager, ClientConnectionSet* clients) { psServerDR::entitymanager = entitymanager; psServerDR::clients = clients; if (!psserver->GetEventManager()->Subscribe(this,MSGTYPE_DEAD_RECKONING,REQUIRE_READY_CLIENT)) return false; calc_damage = psserver->GetMathScriptEngine()->FindScript("Calculate Fall Damage"); if(!calc_damage) { CPrintf(CON_ERROR, "Cannot find script 'Calculate Fall Damage'!\n"); return false; } // Output var bindings here var_fall_height = calc_damage->GetVar("FallHeight"); var_fall_dmg = calc_damage->GetVar("Damage"); paladin = new PaladinJr; paladin->Initialize(entitymanager); return true; } void psServerDR::SendPersist() { // no server side actions yet return; } void psServerDR::HandleFallDamage(gemActor *actor,int clientnum, float height) { float fallHeight = actor->FallEnded(height); var_fall_height->SetValue(fallHeight); calc_damage->Execute(); // CPrintf(CON_DEBUG, "Fall damage=%1.2f\n",var_fall_dmg->GetValue() ); if (var_fall_dmg->GetValue() > 0) { bool died = (var_fall_dmg->GetValue() > actor->GetCharacterData()->GetHP()); actor->DoDamage(NULL, var_fall_dmg->GetValue() ); if (died) { //Client died psserver->SendSystemInfo(clientnum, "You fell down and died."); } } } void psServerDR::HandleMessage (MsgEntry* me,Client *client) { psDRMessage drmsg(me,CacheManager::GetSingleton().GetMsgStrings(),EntityManager::GetSingleton().GetEngine() ); if (!drmsg.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psDRMessage from client %u.\n",me->clientnum); return; } gemActor *actor = client->GetActor(); if (actor == NULL) { Error1("Recieved DR data for NULL actor."); return; } if ( !actor->IsAllowedToMove() || client->IsFrozen() ) // Is this movement allowed? { actor->MoveToLastPos(); return; // Ignore this DR data, and force the actor to use last. } if (drmsg.sector == NULL) { Error2("Client sent the server DR message with unknown sector \"%s\" !", drmsg.sectorName.GetData()); psserver->SendSystemInfo(me->clientnum, "Received unknown sector \"%s\" - moving you to a valid position.", drmsg.sectorName.GetData() ); actor->MoveToValidPos(); return; } // These values must be sane or the proxlist will die. // The != test tests for NaN because if it is, the proxlist will mysteriously die (found out the hard way) if(drmsg.pos.x != drmsg.pos.x || drmsg.pos.y != drmsg.pos.y || drmsg.pos.z != drmsg.pos.z || fabs(drmsg.pos.x) > 100000 || fabs(drmsg.pos.y) > 100000 || fabs(drmsg.pos.z) > 100000) { psserver->SendSystemInfo(me->clientnum, "Received out of bounds positional data, resetting your position."); actor->MoveToValidPos(); return; } else if(drmsg.vel.x != drmsg.vel.x || drmsg.vel.y != drmsg.vel.y || drmsg.vel.z != drmsg.vel.z || fabs(drmsg.vel.x) > 1000 || fabs(drmsg.vel.y) > 1000 || fabs(drmsg.vel.z) > 1000) { psserver->SendSystemInfo(me->clientnum, "Received out of bounds velocity data, resetting your position."); actor->MoveToValidPos(); return; } else if(drmsg.worldVel.x != drmsg.worldVel.x || drmsg.worldVel.y != drmsg.worldVel.y || drmsg.worldVel.z != drmsg.worldVel.z || fabs(drmsg.worldVel.x) > 1000 || fabs(drmsg.worldVel.y) > 1000 || fabs(drmsg.worldVel.z) > 1000) { psserver->SendSystemInfo(me->clientnum, "Received out of bounds velocity data, resetting your position."); actor->MoveToValidPos(); return; } paladin->PredictClient(client, drmsg); // Go ahead and update the server version if (!actor->SetDRData(drmsg)) // out of date message if returns false return; // Check for Movement Tutorial Required. // Usually we don't want to check but DR msgs are so frequent, // perf hit is unacceptable otherwise. if (actor->GetCharacterData()->NeedsHelpEvent(TutorialManager::MOVEMENT)) { if (!drmsg.vel.IsZero() || drmsg.ang_vel) { psMovementEvent evt(client->GetClientNum() ); evt.FireEvent(); } } // Update falling status if (actor->pcmove->IsOnGround()) { if (actor->IsFalling()) { // GM flag if(!actor->safefall) HandleFallDamage(actor,me->clientnum, drmsg.pos.y); else actor->FallEnded(drmsg.pos.y); } } else if (!actor->IsFalling()) { //actor->pcmove->ClearPortalDisplacement(); // Perform the extrapolation here to find warp portals //if(drmsg.vel.y > -50 && drmsg.vel.y < 50) // Sanity check // actor->pcmove->UpdateDRDelta(1100); iSector* sector = actor->GetMeshWrapper()->GetMovable()->GetSectors()->Get(0); actor->FallBegan(drmsg.pos.y, 0.0f, sector); // Reset to correct position //actor->SetDRData(drmsg); } //Check if client fell off the end of the world or has left it if (drmsg.pos.y < -1000 && drmsg.worldVel.y < 0) { actor->Kill(NULL); psserver->SendSystemInfo(me->clientnum, "You fell off the end of the world and died."); } else if (drmsg.pos.y > 1000 && drmsg.vel.y > 0) { actor->Kill(NULL); psserver->SendSystemInfo(me->clientnum, "You flew off the end of the world and died."); } // Now multicast to other clients csTicks time = csGetTicks(); actor->UpdateProxList(); if (csGetTicks() - time > 500) { csString status; status.Format("Warning: Spent %u time updating proxlist for %s!", csGetTicks() - time, actor->GetName()); psString out; actor->GetProxList()->DebugDumpContents(out); out.ReplaceAllSubString("\n", " "); status.Append(out); psserver->GetLogCSV()->Write(CSV_STATUS, status); } psserver->GetEventManager()->Multicast(me, actor->GetMulticastClients(), me->clientnum,PROX_LIST_ANY_RANGE); paladin->CheckClient(client); if (strcmp(drmsg.sector->QueryObject()->GetName(), "DRexit") == 0) { actor->pcmove->SetOnGround(false); actor->MoveToSpawnPos(); } }