Buildings now have simple type distinctions to work using the known output of a SpawnRequestMessage packet. Support for cross-continental respawning (actually a failure case for being unable to spawn). Corpse tuning and testing.

This commit is contained in:
FateJH 2018-03-24 00:28:02 -04:00
parent 20b7726653
commit ddc2450541
52 changed files with 2491 additions and 711 deletions

View file

@ -6,7 +6,7 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, WarpGate}
import net.psforever.objects.serverobject.structures.{Building, FoundationBuilder, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.types.Vector3
@ -23,7 +23,7 @@ object Maps {
val map5 = new ZoneMap("map05")
val map6 = new ZoneMap("map06") {
LocalBuilding(2, FoundationBuilder(Building.Structure)) //Anguta
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Facility, Vector3(3974.2344f, 4287.914f, 0)))) //Anguta
LocalObject(222, Door.Constructor) //air term building, bay door
LocalObject(370, Door.Constructor) //courtyard
LocalObject(371, Door.Constructor) //courtyard
@ -193,17 +193,17 @@ object Maps {
TerminalToSpawnPad(224, 501)
TerminalToSpawnPad(2419, 500)
LocalBuilding(38, FoundationBuilder(Building.Structure)) //Anguta, West Bunker
LocalBuilding(38, FoundationBuilder(Building.Structure(StructureType.Bunker))) //Anguta, West Bunker
LocalObject(362, Door.Constructor)
ObjectToBuilding(362, 38)
LocalBuilding(42, FoundationBuilder(Building.Structure)) //Anguta, East Bunker(s)
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Bunker))) //Anguta, East Bunker(s)
LocalObject(407, Door.Constructor)
LocalObject(408, Door.Constructor)
ObjectToBuilding(407, 42)
ObjectToBuilding(408, 42)
LocalBuilding(48, FoundationBuilder(Building.Structure)) //North Anguta Watchtower
LocalBuilding(48, FoundationBuilder(Building.Structure(StructureType.Tower, Vector3(3864.2266f, 4518.0234f, 0)))) //North Anguta Watchtower
LocalObject(364, Door.Constructor(Vector3(3871.9688f, 4509.992f, 269.65625f), Vector3(0f, 0f, 180f))) //s1
LocalObject(365, Door.Constructor(Vector3(3871.9688f, 4509.992f, 279.57812f), Vector3(0f, 0f, 180f))) //s2
LocalObject(366, Door.Constructor(Vector3(3871.9688f, 4509.992f, 299.57812f), Vector3(0f, 0f, 180f))) //s3
@ -227,8 +227,8 @@ object Maps {
LocalObject(1561, Terminal.Constructor(order_terminal))
LocalObject(1562, Terminal.Constructor(order_terminal))
LocalObject(1563, Terminal.Constructor(order_terminal))
LocalObject(2138, SpawnTube.Constructor(Vector3(3870.9688f, 4505.7266f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2139, SpawnTube.Constructor(Vector3(3870.9688f, 4522.1562f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2138, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4505.7266f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2139, SpawnTube.Constructor(respawn_tube_tower, Vector3(3870.9688f, 4522.1562f, 259.875f), Vector3(0, 0, 90)))
LocalObject(2315, Door.Constructor) //spawn tube door
LocalObject(2316, Door.Constructor) //spawn tube door
ObjectToBuilding(364, 48)
@ -287,15 +287,13 @@ object Maps {
LocalObject(1081, Terminal.Constructor(implant_terminal_interface)) //tube 520
TerminalToInterface(520, 1081)
LocalBuilding(2, FoundationBuilder(Building.Structure)) //HART building C
LocalBuilding(2, FoundationBuilder(Building.Structure(StructureType.Building))) //HART building C
LocalObject(186, Terminal.Constructor(cert_terminal))
LocalObject(187, Terminal.Constructor(cert_terminal))
LocalObject(188, Terminal.Constructor(cert_terminal))
LocalObject(362, Door.Constructor)
LocalObject(370, Door.Constructor)
LocalObject(371, Door.Constructor)
LocalObject(372, Door.Constructor)
LocalObject(373, Door.Constructor)
LocalObject(374, Door.Constructor)
LocalObject(375, Door.Constructor)
LocalObject(394, Door.Constructor)
@ -303,6 +301,8 @@ object Maps {
LocalObject(396, Door.Constructor)
LocalObject(397, Door.Constructor)
LocalObject(398, Door.Constructor)
LocalObject(462, Door.Constructor)
LocalObject(463, Door.Constructor)
LocalObject(522, ImplantTerminalMech.Constructor)
LocalObject(523, ImplantTerminalMech.Constructor)
LocalObject(524, ImplantTerminalMech.Constructor)
@ -337,8 +337,6 @@ object Maps {
ObjectToBuilding(362, 2)
ObjectToBuilding(370, 2)
ObjectToBuilding(371, 2)
ObjectToBuilding(372, 2)
ObjectToBuilding(373, 2)
ObjectToBuilding(374, 2)
ObjectToBuilding(375, 2)
ObjectToBuilding(394, 2)
@ -346,6 +344,8 @@ object Maps {
ObjectToBuilding(396, 2)
ObjectToBuilding(397, 2)
ObjectToBuilding(398, 2)
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(522, 2)
ObjectToBuilding(523, 2)
ObjectToBuilding(524, 2)
@ -383,7 +383,7 @@ object Maps {
TerminalToInterface(528, 1088)
TerminalToInterface(529, 1089)
LocalBuilding(29, FoundationBuilder(Building.Structure)) //South Villa Gun Tower
LocalBuilding(29, FoundationBuilder(Building.Structure(StructureType.Tower))) //South Villa Gun Tower
LocalObject(330, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 91.140625f), Vector3(0, 0, 180)))
LocalObject(331, Door.Constructor(Vector3(3979.9219f, 2592.0547f, 111.140625f), Vector3(0, 0, 180)))
LocalObject(332, Door.Constructor(Vector3(3979.9688f, 2608.0625f, 91.140625f), Vector3(0, 0, 0)))
@ -405,7 +405,61 @@ object Maps {
DoorToLock(332, 556)
DoorToLock(333, 557)
LocalBuilding(51, FoundationBuilder(Building.Structure))
LocalBuilding(42, FoundationBuilder(Building.Structure(StructureType.Building, Vector3(1, 0, 0)))) //spawn building south of HART C
LocalObject(258, Door.Constructor) //spawn tube door
LocalObject(259, Door.Constructor) //spawn tube door
LocalObject(260, Door.Constructor) //spawn tube door
LocalObject(261, Door.Constructor) //spawn tube door
LocalObject(262, Door.Constructor) //spawn tube door
LocalObject(263, Door.Constructor) //spawn tube door
LocalObject(372, Door.Constructor) //entrance
LocalObject(373, Door.Constructor) //entrance
LocalObject(430, Door.Constructor) //vr door
LocalObject(431, Door.Constructor) //vr door
LocalObject(432, Door.Constructor) //vr door
LocalObject(433, Door.Constructor) //vr door
LocalObject(434, Door.Constructor) //vr door
LocalObject(435, Door.Constructor) //vr door
LocalObject(744, SpawnTube.Constructor(Vector3(3684.336f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
LocalObject(745, SpawnTube.Constructor(Vector3(3684.336f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
LocalObject(746, SpawnTube.Constructor(Vector3(3691.0703f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
LocalObject(747, SpawnTube.Constructor(Vector3(3691.0703f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
LocalObject(748, SpawnTube.Constructor(Vector3(3697.711f, 2709.0469f, 91.859375f), Vector3(0, 0, 180)))
LocalObject(749, SpawnTube.Constructor(Vector3(3697.711f, 2713.2344f, 91.859375f), Vector3(0, 0, 0)))
LocalObject(852, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(853, Terminal.Constructor(order_terminal)) //n. wall
LocalObject(854, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(855, Terminal.Constructor(order_terminal)) //n. wall
LocalObject(859, Terminal.Constructor(order_terminal)) //s. wall
LocalObject(860, Terminal.Constructor(order_terminal)) //n. wall
ObjectToBuilding(258, 42)
ObjectToBuilding(259, 42)
ObjectToBuilding(260, 42)
ObjectToBuilding(261, 42)
ObjectToBuilding(262, 42)
ObjectToBuilding(263, 42)
ObjectToBuilding(372, 42)
ObjectToBuilding(373, 42)
ObjectToBuilding(430, 42)
ObjectToBuilding(431, 42)
ObjectToBuilding(432, 42)
ObjectToBuilding(433, 42)
ObjectToBuilding(434, 42)
ObjectToBuilding(435, 42)
ObjectToBuilding(744, 42)
ObjectToBuilding(745, 42)
ObjectToBuilding(746, 42)
ObjectToBuilding(747, 42)
ObjectToBuilding(748, 42)
ObjectToBuilding(749, 42)
ObjectToBuilding(852, 42)
ObjectToBuilding(853, 42)
ObjectToBuilding(854, 42)
ObjectToBuilding(855, 42)
ObjectToBuilding(859, 42)
ObjectToBuilding(860, 42)
LocalBuilding(51, FoundationBuilder(Building.Structure(StructureType.Platform))) //air terminal west of HART C
LocalObject(304, Terminal.Constructor(dropship_vehicle_terminal))
LocalObject(292,
VehicleSpawnPad.Constructor(Vector3(3508.9844f, 2895.961f, 92.296875f), Vector3(0f, 0f, 270.0f))
@ -414,7 +468,7 @@ object Maps {
ObjectToBuilding(292, 51)
TerminalToSpawnPad(304, 292)
LocalBuilding(77, FoundationBuilder(Building.Structure))
LocalBuilding(77, FoundationBuilder(Building.Structure(StructureType.Platform))) //ground terminal west of HART C
LocalObject(1063, Terminal.Constructor(ground_vehicle_terminal))
LocalObject(706,
VehicleSpawnPad.Constructor(Vector3(3506.0f, 2820.0f, 92.0f), Vector3(0f, 0f, 270.0f))
@ -422,18 +476,6 @@ object Maps {
ObjectToBuilding(1063, 77)
ObjectToBuilding(706, 77)
TerminalToSpawnPad(1063, 706)
//TODO check building id: these belong to a spawn building in HART C campus
LocalObject(462, Door.Constructor)
LocalObject(463, Door.Constructor)
LocalObject(853, Terminal.Constructor(order_terminal))
LocalObject(855, Terminal.Constructor(order_terminal))
LocalObject(860, Terminal.Constructor(order_terminal))
ObjectToBuilding(462, 2)
ObjectToBuilding(463, 2)
ObjectToBuilding(853, 2)
ObjectToBuilding(855, 2)
ObjectToBuilding(860, 2)
}
val map14 = new ZoneMap("map13")

View file

@ -25,10 +25,10 @@ import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech
import net.psforever.objects.serverobject.locks.IFFLock
import net.psforever.objects.serverobject.mblocker.Locker
import net.psforever.objects.serverobject.pad.VehicleSpawnPad
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, SpawnTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.{MatrixTerminalDefinition, Terminal}
import net.psforever.objects.serverobject.terminals.Terminal.TerminalMessage
import net.psforever.objects.vehicles.{AccessPermissionGroup, Utility, VehicleLockState}
import net.psforever.objects.serverobject.structures.{Building, WarpGate}
import net.psforever.objects.serverobject.structures.{Building, StructureType, WarpGate}
import net.psforever.objects.serverobject.terminals.Terminal
import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.vehicles.{AccessPermissionGroup, VehicleLockState}
@ -56,6 +56,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
var taskResolver : ActorRef = Actor.noSender
var galaxy : ActorRef = Actor.noSender
var continent : Zone = null
var player : Player = null
var avatar : Avatar = null
var progressBarValue : Option[Float] = None
var shooting : Option[PlanetSideGUID] = None
var accessedContainer : Option[PlanetSideGameObject with Container] = None
@ -66,9 +68,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
var clientKeepAlive : Cancellable = DefaultCancellable.obj
var progressBarUpdate : Cancellable = DefaultCancellable.obj
var reviveTimer : Cancellable = DefaultCancellable.obj
override def postStop() = {
clientKeepAlive.cancel()
clientKeepAlive.cancel
progressBarUpdate.cancel
reviveTimer.cancel
localService ! Service.Leave()
vehicleService ! Service.Leave()
avatarService ! Service.Leave()
@ -282,7 +287,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case AvatarResponse.Release(tplayer) =>
if(tplayer_guid != guid) {
turnPlayerIntoCorpse(tplayer)
TurnPlayerIntoCorpse(tplayer)
}
case AvatarResponse.Reload(item_guid) =>
@ -920,7 +925,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.Transport ! Zone.SpawnVehicle(vehicle)
vehicleService ! VehicleServiceMessage(continent.Id, VehicleAction.LoadVehicle(player_guid, vehicle, objedtId, vehicle_guid, vdata))
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 22, 1L)) //mount points off?
sendResponse(PlanetsideAttributeMessage(vehicle_guid, 21, player_guid.guid)) //fte and ownership?
//sendResponse(ObjectAttachMessage(vehicle_guid, player_guid, 0))
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(vehicle_guid) //cancel queue timeout delay
@ -1002,6 +1006,64 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.info(s"$tplayer has left zone ${zone.Id}")
}
case Zone.Lattice.SpawnPoint(zone_id, building, spawn_tube) =>
log.info(s"Zone.Lattice.SpawnPoint: spawn point on $zone_id in ${building.Id} @ ${spawn_tube.GUID.guid} selected")
reviveTimer.cancel
val sameZone = zone_id == continent.Id
val backpack = player.isBackpack
val respawnTime : Long = if(sameZone) { 10 } else { 0 } //s
val respawnTimeMillis = respawnTime * 1000 //ms
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, respawnTimeMillis, respawnTimeMillis, Vector3.Zero, 2, true))
val tplayer = if(backpack) {
RespawnClone(player) //new player
}
else {
val player_guid = player.GUID
sendResponse(ObjectDeleteMessage(player_guid, 4))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 4))
player //player is deconstructing self
}
tplayer.Position = spawn_tube.Position
tplayer.Orientation = spawn_tube.Orientation
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
val (target, msg) : (ActorRef, Any) = if(sameZone) {
if(backpack) {
//respawning from unregistered player
(taskResolver, RegisterAvatar(tplayer))
}
else {
//move existing player
(self, PlayerLoaded(tplayer))
}
}
else {
continent.Population ! Zone.Population.Leave(avatar)
val original = player
//TODO check player orientation upon spawn not polluted
if(backpack) {
//unregister avatar locker + GiveWorld
player = tplayer
(taskResolver, TaskBeforeZoneChange(GUIDTask.UnregisterLocker(original.Locker)(continent.GUID), zone_id))
}
else {
//unregister avatar whole + GiveWorld
(taskResolver, TaskBeforeZoneChange(GUIDTask.UnregisterAvatar(original)(continent.GUID), zone_id))
}
}
context.system.scheduler.scheduleOnce(respawnTime seconds, target, msg)
case Zone.Lattice.NoValidSpawnPoint(zone_number, None) =>
log.warn(s"Zone.Lattice.SpawnPoint: zone $zone_number could not be accessed as requested")
reviveTimer.cancel
RequestSanctuaryZoneSpawn(player, zone_number)
case Zone.Lattice.NoValidSpawnPoint(zone_number, Some(spawn_group)) =>
log.warn(s"Zone.Lattice.SpawnPoint: zone $zone_number has no available ${player.Faction} targets in spawn group $spawn_group")
reviveTimer.cancel
RequestSanctuaryZoneSpawn(player, zone_number)
case InterstellarCluster.ClientInitializationComplete() =>
LivePlayerList.Add(sessionId, avatar)
//PropertyOverrideMessage
@ -1012,21 +1074,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
galaxy ! InterstellarCluster.GetWorld("z6")
case InterstellarCluster.GiveWorld(zoneId, zone) =>
log.info(s"Zone $zoneId has been loaded")
log.info(s"Zone $zoneId will now load")
player.Continent = zoneId
continent = zone
continent.Population ! Zone.Population.Join(avatar)
taskResolver ! RegisterNewAvatar(player)
case NewPlayerLoaded(tplayer) =>
log.info(s"Player $tplayer has been loaded")
log.info(s"Player ${tplayer.Name} has been loaded")
player = tplayer
//LoadMapMessage will cause the client to send back a BeginZoningMessage packet (see below)
sendResponse(LoadMapMessage(continent.Map.Name, continent.Id, 40100,25,true,3770441820L))
AvatarCreate() //important! the LoadMapMessage must be processed by the client before the avatar is created
case PlayerLoaded(tplayer) =>
log.info(s"Player $tplayer has been loaded")
log.info(s"Player ${tplayer.Name} will respawn")
player = tplayer
AvatarCreate()
self ! SetCurrentAvatar(tplayer)
@ -1034,22 +1096,25 @@ class WorldSessionActor extends Actor with MDCContextAware {
case PlayerFailedToLoad(tplayer) =>
player.Continent match {
case _ =>
failWithError(s"$tplayer failed to load anywhere")
failWithError(s"${tplayer.Name} failed to load anywhere")
}
case SetCurrentAvatar(tplayer) =>
player = tplayer
val guid = tplayer.GUID
sendResponse(SetCurrentAvatarMessage(guid,0,0))
if(spectator) {
sendResponse(ChatMsg(ChatMessageType.CMT_TOGGLESPECTATORMODE, false, "", "on", None))
}
(0 until DetailedCharacterData.numberOfImplantSlots(tplayer.BEP)).foreach(slot => {
sendResponse(AvatarImplantMessage(guid, ImplantAction.Initialization, slot, 1)) //init implant slot
sendResponse(AvatarImplantMessage(guid, ImplantAction.Activation, slot, 0)) //deactivate implant
//TODO: if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
//TODO if this implant is Installed but does not have shortcut, add to a free slot or write over slot 61/62/63
})
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
//TODO: if Medkit does not have shortcut, add to a free slot or write over slot 64
//TODO if Medkit does not have shortcut, add to a free slot or write over slot 64
sendResponse(CreateShortcutMessage(guid, 1, 0, true, Shortcut.MEDKIT))
sendResponse(ChangeShortcutBankMessage(guid, 0))
//FavoritesMessage
@ -1195,10 +1260,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
var player : Player = null
var avatar : Avatar = null
var spawnZones : Map[Int, Building] = null
def handleGamePkt(pkt : PlanetSideGamePacket) = pkt match {
case ConnectToWorldRequestMessage(server, token, majorVersion, minorVersion, revision, buildDate, unk) =>
val clientVersion = s"Client Version: $majorVersion.$minorVersion.$revision, $buildDate"
@ -1272,7 +1333,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TODO check if can spawn on last continent/location from player?
//TODO if yes, get continent guid accessors
//TODO if no, get sanctuary guid accessors and reset the player's expectations
//galaxy ! InterstellarCluster.GetWorld("z6")
galaxy ! InterstellarCluster.RequestClientInitialization()
case default =>
log.error("Unsupported " + default + " in " + msg)
@ -1283,18 +1343,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ BeginZoningMessage() =>
log.info("Reticulating splines ...")
configZone(continent) //todo density
configZone(continent)
sendResponse(TimeOfDayMessage(1191182336))
/** WIP */
spawnZones = Map(
7 -> continent.Building(2).get,
6 -> continent.Building(48).get
)
//custom
sendResponse(ContinentalLockUpdateMessage(13, PlanetSideEmpire.VS)) // "The VS have captured the VS Sanctuary."
sendResponse(ReplicationStreamMessage(5, Some(6), Vector(SquadListing()))) //clear squad list
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1))
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 112, 1)) //common
(0 to 255).foreach(i => { sendResponse(SetEmpireMessage(PlanetSideGUID(i), PlanetSideEmpire.VS)) })
//render Equipment that was dropped into zone before the player arrived
continent.EquipmentOnGround.foreach(item => {
@ -1309,20 +1365,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
})
//load active players in zone
continent.LivePlayers.filterNot(_.GUID == player.GUID).foreach(char => {
sendResponse(
ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get)
)
sendResponse(ObjectCreateMessage(ObjectClass.avatar, char.GUID, char.Definition.Packet.ConstructorData(char).get))
})
//load corpses in zone
continent.Corpses.foreach( turnPlayerIntoCorpse(_) )
continent.Corpses.foreach { TurnPlayerIntoCorpse }
//load active vehicles in zone
continent.Vehicles.foreach(vehicle => {
val definition = vehicle.Definition
sendResponse(
ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get)
)
sendResponse(ObjectCreateMessage(definition.ObjectId, vehicle.GUID, definition.Packet.ConstructorData(vehicle).get))
//seat vehicle occupants
vehicle.Definition.MountPoints.values.foreach(seat_num => {
definition.MountPoints.values.foreach(seat_num => {
vehicle.Seat(seat_num).get.Occupant match {
case Some(tplayer) =>
if(tplayer.HasGUID) {
@ -1339,14 +1391,12 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.GUID(interface_guid) match {
case Some(obj : Terminal) =>
val objDef = obj.Definition
val obj_uid = objDef.ObjectId
val obj_data = objDef.Packet.ConstructorData(obj).get
sendResponse(
ObjectCreateMessage(
obj_uid,
ObjectClass.implant_terminal_interface,
PlanetSideGUID(interface_guid),
ObjectCreateMessageParent(parent_guid, 1),
obj_data
objDef.Packet.ConstructorData(obj).get
)
)
case _ => ;
@ -1444,36 +1494,35 @@ class WorldSessionActor extends Actor with MDCContextAware {
case msg @ ReleaseAvatarRequestMessage() =>
log.info(s"ReleaseAvatarRequest: ${player.GUID} on ${continent.Id} has released")
//TODO is it easier to delete the player, then re-create them as a corpse?
reviveTimer.cancel
player.Release
continent.Population ! Zone.Population.Release(avatar)
continent.Population ! Zone.Corpse.Add(player)
val knife = player.Slot(4).Equipment.get
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
turnPlayerIntoCorpse(player)
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))
continent.Population ! Zone.Population.Release(avatar)
player.VehicleSeated match {
case None =>
continent.Population ! Zone.Corpse.Add(player) //TODO move back out of this match case when changing below issue
val knife = player.Slot(4).Equipment.get
player.Slot(4).Equipment = None
taskResolver ! RemoveEquipmentFromSlot(player, knife, 4)
TurnPlayerIntoCorpse(player)
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.Release(player, continent))
case Some(_) =>
//TODO we do not want to delete the player if he is seated in a vehicle when releasing
//TODO it is necessary for now until we know how to juggle ownership properly
val player_guid = player.GUID
sendResponse(ObjectDeleteMessage(player_guid, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ObjectDelete(player_guid, player_guid, 0))
self ! PacketCoding.CreateGamePacket(0, DismountVehicleMsg(player_guid, 0, true)) //let vehicle try to clean up its fields
taskResolver ! GUIDTask.UnregisterPlayer(player)(continent.GUID)
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0, 0, 0))
//sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
}
case msg @ SpawnRequestMessage(u1, u2, u3, u4, u5) =>
log.info(s"SpawnRequestMessage: $msg")
spawnZones.get(u2.toInt) match {
case Some(building) =>
scala.util.Random.shuffle(building.Amenities.filter(_.isInstanceOf[SpawnTube])).headOption match { //TODO temporary shuffle
case Some(tube) =>
val tplayer = SpawnRequest(player) //new player
tplayer.Position = tube.Position
tplayer.Orientation = tube.Orientation
log.info(s"SpawnRequestMessage: new player will spawn in ${building.Id} @ ${tube.GUID.guid}")
sendResponse(AvatarDeadStateMessage(DeadState.RespawnTime, 10000, 10000, Vector3.Zero, 2, true))
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(10 seconds, taskResolver, RegisterAvatar(tplayer))
case None =>
log.warn(s"SpawnRequestMessage: can not find a spawn point in this spawn group - $u2")
}
case None =>
log.warn(s"SpawnRequestMessage: can not find somewhere to spawn on ${continent.Id}")
}
//TODO just focus on u5 and u2 for now
galaxy ! Zone.Lattice.RequestSpawnPoint(u5.toInt, player, u2.toInt)
case msg @ SetChatFilterMessage(send_channel, origin, whitelist) =>
log.info("SetChatFilters: " + msg)
@ -1516,15 +1565,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
if(messagetype == ChatMessageType.CMT_SUICIDE) {
val player_guid = player.GUID
val pos = player.Position
player.Die
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 2, 0))
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
sendResponse(AvatarDeadStateMessage(DeadState.Dead, 300000, 300000, pos, 2, true))
KillPlayer(player)
}
if(messagetype == ChatMessageType.CMT_DESTROY) {
self ! PacketCoding.CreateGamePacket(0, RequestDestroyMessage(PlanetSideGUID(contents.toInt)))
}
if (messagetype == ChatMessageType.CMT_VOICE) {
@ -1853,7 +1898,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
// TODO: Make sure this is the correct response for all cases
continent.GUID(object_guid) match {
case Some(vehicle : Vehicle) =>
if(player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID)) {
if((player.VehicleOwned.contains(object_guid) && vehicle.Owner.contains(player.GUID))
|| (player.Faction == vehicle.Faction && (vehicle.Owner.isEmpty || vehicle.Health == 0))) {
vehicleService ! VehicleServiceMessage.UnscheduleDeconstruction(object_guid)
vehicleService ! VehicleServiceMessage.RequestDeleteVehicle(vehicle, continent)
log.info(s"RequestDestroy: vehicle $object_guid")
@ -2084,7 +2130,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
case Some(obj : Terminal) =>
if(obj.Definition.isInstanceOf[MatrixTerminalDefinition] || obj.Definition.isInstanceOf[SpawnTerminalDefinition]) {
if(obj.Definition.isInstanceOf[MatrixTerminalDefinition]) {
//TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks)
sendResponse(BindPlayerMessage(1, "@ams", true, true, 0, 0, 0, obj.Position))
}
@ -2092,6 +2138,13 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
}
case Some(obj : SpawnTube) =>
//deconstruction
PlayerActionsToCancel()
player.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, 2, true))
continent.Population ! Zone.Population.Release(avatar)
case Some(obj : PlanetSideGameObject) =>
if(itemType != 121) {
sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType))
@ -2217,7 +2270,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
def dismountWarning(msg : String) : Unit = {
log.warn(s"$msg; some vehicle might not know that a player is no longer sitting in it")
}
if(player.GUID == player_guid) {
if(player.HasGUID && player.GUID == player_guid) {
//normally disembarking from a seat
player.VehicleSeated match {
case Some(obj_guid) =>
@ -2562,32 +2615,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
override def onFailure(ex : Throwable) : Unit = {
localAnnounce ! PlayerFailedToLoad(localPlayer) //alerts WSA
}
}, List(RegisterLightweightAvatar(tplayer)(continent.GUID))
}, List(GUIDTask.RegisterPlayer(tplayer)(continent.GUID))
)
}
//TODO temporary function for registering avatar without locker contents
def RegisterLightweightAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer
import net.psforever.objects.inventory.InventoryItem
val holsterTasks = tplayer.Holsters().filter(_.Equipment.isDefined).map(slot =>{
GUIDTask.RegisterEquipment(slot.Equipment.get)(guid)
}).toList
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => GUIDTask.RegisterEquipment(entry.obj)(guid)})
TaskResolver.GiveTask(GUIDTask.RegisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
}
//TODO temporary function for unregistering avatar without locker contents
def UnregisterLightweightAvatar(tplayer : Player)(implicit guid : ActorRef) : TaskResolver.GiveTask = {
import net.psforever.objects.LockerContainer
import net.psforever.objects.inventory.InventoryItem
val holsterTasks = tplayer.Holsters().filter(_.Equipment.isDefined).map(slot =>{
GUIDTask.UnregisterEquipment(slot.Equipment.get)(guid)
}).toList
val inventoryTasks = tplayer.Inventory.Items.map({ case((_ : Int, entry : InventoryItem)) => GUIDTask.UnregisterEquipment(entry.obj)(guid)})
TaskResolver.GiveTask(GUIDTask.UnregisterObjectTask(tplayer)(guid).task, holsterTasks ++ inventoryTasks)
}
/**
* Construct tasking that adds a completed and registered vehicle into the scene.
* Use this function to renew the globally unique identifiers on a vehicle that has already been added to the scene once.
@ -2739,6 +2770,22 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
def TaskBeforeZoneChange(priorTask : TaskResolver.GiveTask, zoneId : String) : TaskResolver.GiveTask = {
TaskResolver.GiveTask(
new Task() {
private val localService = galaxy
private val localMsg = InterstellarCluster.GetWorld(zoneId)
override def isComplete : Task.Resolution.Value = Task.Resolution.Success
def Execute(resolver : ActorRef) : Unit = {
localService ! localMsg
resolver ! scala.util.Success(this)
}
}, List(priorTask)
)
}
/**
* After a client has connected to the server, their account is used to generate a list of characters.
* On the character selection screen, each of these characters is made to exist temporarily when one is selected.
@ -3223,10 +3270,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param building the building object
*/
def initBuilding(continentNumber : Int, buildingNumber : Int, building : Building) : Unit = {
building match {
case _ : WarpGate =>
building.BuildingType match {
case StructureType.WarpGate =>
initGate(continentNumber, buildingNumber, building)
case _ : Building =>
case _ =>
initFacility(continentNumber, buildingNumber, building)
}
}
@ -3309,6 +3356,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(BroadcastWarpgateUpdateMessage(continentNumber, buildingNumber, false, false, true))
}
/**
* Configure the buildings and each specific amenity for that building in a given zone by sending the client packets.
* These actions are performed during the loading of a zone.
* @see `SetEmpireMessage`<br>
* `PlanetsideAttributeMessage`<br>
* `HackMessage`
* @param zone the zone being loaded
*/
def configZone(zone : Zone) : Unit = {
zone.Buildings.values.foreach(building => {
sendResponse(SetEmpireMessage(PlanetSideGUID(building.ModelId), building.Faction))
@ -3322,11 +3377,90 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
/**
* TODO write
* The player has lost all his vitality and must be killed.<br>
* <br>
* Shift directly into a state of being dead on the client by setting health to zero points,
* whereupon the player will perform a dramatic death animation.
* Stamina is also set to zero points.
* If the player was in a vehicle at the time of demise, special conditions apply and
* the model must be manipulated so it behaves correctly.
* Do not move or completely destroy the `Player` object as its coordinates of death will be important.<br>
* <br>
* A maximum revive waiting timer is started.
* When this timer reaches zero, the avatar will attempt to spawn back on its faction-specific sanctuary continent.
* @pararm tplayer the player to be killed
*/
def KillPlayer(tplayer : Player) : Unit = {
val player_guid = tplayer.GUID
val pos = tplayer.Position
val respawnTimer = 300000 //milliseconds
tplayer.Die
sendResponse(PlanetsideAttributeMessage(player_guid, 0, 0))
sendResponse(PlanetsideAttributeMessage(player_guid, 2, 0))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 0, 0))
sendResponse(DestroyMessage(player_guid, player_guid, PlanetSideGUID(0), pos)) //how many players get this message?
sendResponse(AvatarDeadStateMessage(DeadState.Dead, respawnTimer, respawnTimer, pos, 2, true))
if(tplayer.VehicleSeated.nonEmpty) {
//make player invisible (if not, the cadaver sticks out the side in a seated position)
sendResponse(PlanetsideAttributeMessage(player_guid, 29, 1))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.PlanetsideAttribute(player_guid, 29, 1))
}
PlayerActionsToCancel()
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, galaxy, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(tplayer.Faction), tplayer, 7))
}
/**
* An event has occurred that would cause the player character to stop certain stateful activities.
* These activities include shooting, hacking, accessing (a container), flying, and running.
* Other players in the same zone must be made aware that the player has stopped as well.<br>
* <br>
* Things whose configuration should not be changed:<br>
* - if the player is seated
*/
def PlayerActionsToCancel() : Unit = {
progressBarUpdate.cancel
progressBarValue = None
accessedContainer match {
case Some(obj : Vehicle) =>
if(obj.AccessingTrunk.contains(player.GUID)) {
obj.AccessingTrunk = None
UnAccessContents(obj)
}
accessedContainer = None
case Some(_) =>
accessedContainer = None
case None => ;
}
shooting match {
case Some(guid) =>
sendResponse(ChangeFireStateMessage_Stop(guid))
avatarService ! AvatarServiceMessage(continent.Id, AvatarAction.ChangeFireState_Stop(player.GUID, guid))
shooting = None
case None => ;
}
if(flying) {
sendResponse(ChatMsg(ChatMessageType.CMT_FLY, false, "", "off", None))
flying = false
}
if(speed > 1) {
sendResponse(ChatMsg(ChatMessageType.CMT_SPEED, false, "", "1.000", None))
speed = 1f
}
}
/**
* A part of the process of spawning the player into the game world.
* The function should work regardless of whether the player is alive or dead - it will make them alive.
* It adds the `WSA`-current `Player` to the current zone and sends out the expected packets.
*/
def AvatarCreate() : Unit = {
player.Spawn
player.Health = 50
player.Health = 50 //TODO temp
val packet = player.Definition.Packet
val dcdata = packet.DetailedConstructorData(player).get
sendResponse(ObjectCreateDetailedMessage(ObjectClass.avatar, player.GUID, dcdata))
@ -3335,11 +3469,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
log.debug(s"ObjectCreateDetailedMessage: $dcdata")
}
def SpawnRequest(tplayer : Player) : Player = {
/**
* Produce a clone of the player that is equipped with the default infantry loadout.
* The loadout is hardcoded.
* The player is expected to be in a Standard Exo-Suit.
* @param tplayer the original player
* @return the duplication of the player, in Standard Exo-Suit and with default equipment loadout
*/
def RespawnClone(tplayer : Player) : Player = {
val faction = tplayer.Faction
val obj = Player.Respawn(tplayer)
//obj.VehicleOwned = tplayer.VehicleOwned
//obj.Continent = tplayer.Continent
obj.Slot(0).Equipment = Tool(StandardPistol(faction))
obj.Slot(2).Equipment = Tool(suppressor)
obj.Slot(4).Equipment = Tool(StandardMelee(faction))
@ -3352,13 +3491,38 @@ class WorldSessionActor extends Actor with MDCContextAware {
obj
}
def turnPlayerIntoCorpse(tplayer : Player) : Unit = {
//sendResponse(PlanetsideAttributeMessage(tplayer.GUID, 6, 1))
/**
* Creates a player that has the characteristics of a corpse.
* To the game, that is a backpack (or some pastry, festive graphical modification allowing).
* @see `CorpseConverter.converter`
* @param tplayer the player
*/
def TurnPlayerIntoCorpse(tplayer : Player) : Unit = {
sendResponse(
ObjectCreateDetailedMessage(ObjectClass.avatar, tplayer.GUID, CorpseConverter.converter.DetailedConstructorData(tplayer).get)
)
}
/**
* Attempt to tranfer to the player's faction-specific sanctuary continent.
* If the server thinks the player is already on his sanctuary continent,
* it will disconnect the player under the assumption that an error has occurred.
* Eventually, this functionality should support better error-handling before it jumps to the conclusion:
* "Disconnecting the client is the safest option."
* @see `Zones.SanctuaryZoneNumber`
* @param tplayer the player
* @param currentZone the current cone number
*/
def RequestSanctuaryZoneSpawn(tplayer : Player, currentZone : Int) : Unit = {
val sanctNumber = Zones.SanctuaryZoneNumber(tplayer.Faction)
if(currentZone == sanctNumber) {
sendResponse(DisconnectMessage("Player failed to load on faction's sanctuary continent. Please relog."))
}
else {
galaxy ! Zone.Lattice.RequestSpawnPoint(sanctNumber, tplayer, 7)
}
}
def failWithError(error : String) = {
log.error(error)
sendResponse(ConnectionClose())

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever
import akka.actor.ActorContext
import net.psforever.objects.zones.Zone
import net.psforever.types.PlanetSideEmpire
object Zones {
val z1 = new Zone("z1", Maps.map1, 1)
@ -86,4 +87,32 @@ object Zones {
val i3 = new Zone("i3", Maps.map98, 31)
val i4 = new Zone("i4", Maps.map99, 32)
/**
* Get the zone identifier name for the sanctuary continent of a given empire.
* @param faction the empire
* @return the zone id, with a blank string as an invalidating result
*/
def SanctuaryZoneId(faction : PlanetSideEmpire.Value) : String = {
faction match {
case PlanetSideEmpire.TR => "home1"
case PlanetSideEmpire.NC => "home2"
case PlanetSideEmpire.VS => "home3"
case PlanetSideEmpire.NEUTRAL => "" //invalid, not black ops
}
}
/**
* Get the zone number for the sanctuary continent of a given empire.
* @param faction the empire
* @return the zone number, within the sequence 1-32, and with 0 as an invalidating result
*/
def SanctuaryZoneNumber(faction : PlanetSideEmpire.Value) : Int = {
faction match {
case PlanetSideEmpire.TR => 11
case PlanetSideEmpire.NC => 12
case PlanetSideEmpire.VS => 13
case PlanetSideEmpire.NEUTRAL => 0 //invalid, not black ops
}
}
}

View file

@ -92,7 +92,7 @@ class UndertakerActor extends Actor {
override def onFailure(ex : Throwable): Unit = {
localAnnounce ! UndertakerActor.FailureToWork(localCorpse, localZone, ex)
}
}, List(GUIDTask.UnregisterAvatar(corpse)(zone.GUID))
}, List(GUIDTask.UnregisterPlayer(corpse)(zone.GUID))
)
}