mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-07-13 07:24:53 +00:00
Disconnect (#499)
* better kicking; a quitting that eliminates persistence * GenericActionMessage comments; integer delay time * TeardownConnection corresponds to closing the client directly * messaging path for CMT_QUIT immediate logout that intersects zoning logic for IA and Recall * slightly improved kicking, and the posibility of longer kicking * restoring a turn counter instance * player character will now clean up like normal; immediately turns into corpse; kick delay exists only on the persistence monitor
This commit is contained in:
parent
a5403298e3
commit
e91e282d3a
4 changed files with 254 additions and 75 deletions
|
|
@ -10,7 +10,8 @@ object Zoning {
|
||||||
val
|
val
|
||||||
None,
|
None,
|
||||||
InstantAction,
|
InstantAction,
|
||||||
Recall
|
Recall,
|
||||||
|
Quit
|
||||||
= Value
|
= Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +36,8 @@ object Zoning {
|
||||||
final val Enemy = TimeType(30, "Enemy")
|
final val Enemy = TimeType(30, "Enemy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final case class Quit()
|
||||||
|
|
||||||
object InstantAction {
|
object InstantAction {
|
||||||
final case class Request(faction : PlanetSideEmpire.Value)
|
final case class Request(faction : PlanetSideEmpire.Value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,10 @@ import scodec.codecs._
|
||||||
* 24 - message: you have been imprinted (updates imprinted status; does it?)<br>
|
* 24 - message: you have been imprinted (updates imprinted status; does it?)<br>
|
||||||
* 25 - message: you are no longer imprinted (updates imprinted status; does it?)<br>
|
* 25 - message: you are no longer imprinted (updates imprinted status; does it?)<br>
|
||||||
* 27 - event: purchase timers reset (does it?)<br>
|
* 27 - event: purchase timers reset (does it?)<br>
|
||||||
* 31 - switch to first person view, attempt to deconstruct but fail;
|
* 31 - forced into first person view;
|
||||||
|
* in third person view, player character sinks into the ground; green deconstruction particle effect under feet<br>
|
||||||
|
* 32 - forced into first person view, attempt to deconstruct but fail;
|
||||||
* event: fail to deconstruct due to having a "parent vehicle"<br>
|
* event: fail to deconstruct due to having a "parent vehicle"<br>
|
||||||
* 32 - switch to first person view<br>
|
|
||||||
* 33 - event: fail to deconstruct<br>
|
* 33 - event: fail to deconstruct<br>
|
||||||
* 43 - prompt: friendly fire in virtual reality zone<br>
|
* 43 - prompt: friendly fire in virtual reality zone<br>
|
||||||
* 45 - ?<br>
|
* 45 - ?<br>
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,8 @@ import net.psforever.objects._
|
||||||
import net.psforever.objects.serverobject.mount.Mountable
|
import net.psforever.objects.serverobject.mount.Mountable
|
||||||
import net.psforever.objects.zones.Zone
|
import net.psforever.objects.zones.Zone
|
||||||
import net.psforever.types.Vector3
|
import net.psforever.types.Vector3
|
||||||
import services.{RemoverActor, Service, ServiceManager}
|
import services.{Service, ServiceManager}
|
||||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||||
import services.vehicle.VehicleServiceMessage
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A global service that manages user behavior as divided into the following three categories:
|
* A global service that manages user behavior as divided into the following three categories:
|
||||||
|
|
@ -85,10 +84,34 @@ class AccountPersistenceService extends Actor {
|
||||||
case Some(ref) =>
|
case Some(ref) =>
|
||||||
ref ! msg
|
ref ! msg
|
||||||
case None =>
|
case None =>
|
||||||
log.warn(s"tried to update a player entry ($name) that did not yet exist; rebuilding entry ...")
|
log.warn(s"tried to update a player entry for $name that did not yet exist; rebuilding entry ...")
|
||||||
CreateNewPlayerToken(name).tell(msg, sender)
|
CreateNewPlayerToken(name).tell(msg, sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case msg @ AccountPersistenceService.PersistDelay(name, _) =>
|
||||||
|
accounts.get(name) match {
|
||||||
|
case Some(ref) =>
|
||||||
|
ref ! msg
|
||||||
|
case _ =>
|
||||||
|
log.warn(s"player entry for $name not found; not logged in")
|
||||||
|
}
|
||||||
|
|
||||||
|
case msg @ AccountPersistenceService.Kick(name, _) =>
|
||||||
|
accounts.get(name) match {
|
||||||
|
case Some(ref) =>
|
||||||
|
ref ! msg
|
||||||
|
case _ =>
|
||||||
|
log.warn(s"player entry for $name not found; not logged in")
|
||||||
|
}
|
||||||
|
|
||||||
|
case AccountPersistenceService.Logout(name) =>
|
||||||
|
accounts.remove(name) match {
|
||||||
|
case Some(ref) =>
|
||||||
|
ref ! Logout(name)
|
||||||
|
case _ =>
|
||||||
|
log.warn(s"player entry for $name not found; not logged in")
|
||||||
|
}
|
||||||
|
|
||||||
case Logout(target) => //TODO use context.watch and Terminated?
|
case Logout(target) => //TODO use context.watch and Terminated?
|
||||||
accounts.remove(target)
|
accounts.remove(target)
|
||||||
|
|
||||||
|
|
@ -114,7 +137,7 @@ class AccountPersistenceService extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg =>
|
case msg =>
|
||||||
log.warn(s"Not yet started; received a $msg that will go unhandled")
|
log.warn(s"not yet started; received a $msg that will go unhandled")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -166,6 +189,22 @@ object AccountPersistenceService {
|
||||||
* @param position the location of the player in game world coordinates
|
* @param position the location of the player in game world coordinates
|
||||||
*/
|
*/
|
||||||
final case class Update(name : String, zone : Zone, position : Vector3)
|
final case class Update(name : String, zone : Zone, position : Vector3)
|
||||||
|
|
||||||
|
final case class Kick(name : String, time : Option[Long] = None)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the persistence monitor that was setup for a user for a custom persistence delay.
|
||||||
|
* If set to `None`, the default persistence time should assert itself.
|
||||||
|
* @param name the unique name of the player
|
||||||
|
* @param time the duration that this user's player characters will persist without update in seconds
|
||||||
|
*/
|
||||||
|
final case class PersistDelay(name : String, time : Option[Long])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message that indicates that persistence is no longer necessary for this player character.
|
||||||
|
* @param name the unique name of the player
|
||||||
|
*/
|
||||||
|
final case class Logout(name : String)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,6 +228,12 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
var inZone : Zone = Zone.Nowhere
|
var inZone : Zone = Zone.Nowhere
|
||||||
/** the last-reported game coordinate position of this player */
|
/** the last-reported game coordinate position of this player */
|
||||||
var lastPosition : Vector3 = Vector3.Zero
|
var lastPosition : Vector3 = Vector3.Zero
|
||||||
|
/** */
|
||||||
|
var kicked : Boolean = false
|
||||||
|
/** */
|
||||||
|
var kickTime : Option[Long] = None
|
||||||
|
/** a custom logout time for this player; 60s by default */
|
||||||
|
var persistTime : Option[Long] = None
|
||||||
/** the ongoing amount of permissible inactivity */
|
/** the ongoing amount of permissible inactivity */
|
||||||
var timer : Cancellable = Default.Cancellable
|
var timer : Cancellable = Default.Cancellable
|
||||||
/** the sparingly-used log */
|
/** the sparingly-used log */
|
||||||
|
|
@ -204,17 +249,44 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
|
|
||||||
def receive : Receive = {
|
def receive : Receive = {
|
||||||
case AccountPersistenceService.Login(_) =>
|
case AccountPersistenceService.Login(_) =>
|
||||||
sender ! PlayerToken.LoginInfo(name, inZone, lastPosition)
|
sender ! (if(kicked) {
|
||||||
UpdateTimer()
|
PlayerToken.CanNotLogin(name, PlayerToken.DeniedLoginReason.Kicked)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UpdateTimer()
|
||||||
|
PlayerToken.LoginInfo(name, inZone, lastPosition)
|
||||||
|
})
|
||||||
|
|
||||||
case AccountPersistenceService.Update(_, z, p) =>
|
case AccountPersistenceService.Update(_, z, p) if !kicked =>
|
||||||
inZone = z
|
inZone = z
|
||||||
lastPosition = p
|
lastPosition = p
|
||||||
UpdateTimer()
|
UpdateTimer()
|
||||||
|
|
||||||
|
case AccountPersistenceService.PersistDelay(_, delay) if !kicked =>
|
||||||
|
persistTime = delay
|
||||||
|
UpdateTimer()
|
||||||
|
|
||||||
|
case AccountPersistenceService.Kick(_, time) =>
|
||||||
|
persistTime = None
|
||||||
|
kickTime match {
|
||||||
|
case None if kicked =>
|
||||||
|
UpdateTimer()
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
kicked = true
|
||||||
|
kickTime = time.orElse(Some(300L))
|
||||||
|
|
||||||
case Logout(_) =>
|
case Logout(_) =>
|
||||||
context.parent ! Logout(name)
|
kickTime match {
|
||||||
context.stop(self)
|
case Some(time) =>
|
||||||
|
PerformLogout()
|
||||||
|
kickTime = None
|
||||||
|
timer.cancel
|
||||||
|
timer = context.system.scheduler.scheduleOnce(time seconds, self, Logout(name))
|
||||||
|
case None =>
|
||||||
|
context.parent ! Logout(name)
|
||||||
|
context.stop(self)
|
||||||
|
}
|
||||||
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
|
|
@ -224,7 +296,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
*/
|
*/
|
||||||
def UpdateTimer() : Unit = {
|
def UpdateTimer() : Unit = {
|
||||||
timer.cancel
|
timer.cancel
|
||||||
timer = context.system.scheduler.scheduleOnce(60 seconds, self, Logout(name))
|
timer = context.system.scheduler.scheduleOnce(persistTime.getOrElse(60L) seconds, self, Logout(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -248,7 +320,6 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
* but should be uncommon.
|
* but should be uncommon.
|
||||||
*/
|
*/
|
||||||
def PerformLogout() : Unit = {
|
def PerformLogout() : Unit = {
|
||||||
log.info(s"logout of $name")
|
|
||||||
(inZone.Players.find(p => p.name == name), inZone.LivePlayers.find(p => p.Name == name)) match {
|
(inZone.Players.find(p => p.name == name), inZone.LivePlayers.find(p => p.Name == name)) match {
|
||||||
case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty =>
|
case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty =>
|
||||||
//alive or dead in a vehicle
|
//alive or dead in a vehicle
|
||||||
|
|
@ -336,6 +407,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
squadService.tell(Service.Leave(Some(charId.toString)), parent)
|
squadService.tell(Service.Leave(Some(charId.toString)), parent)
|
||||||
Deployables.Disown(inZone, avatar, parent)
|
Deployables.Disown(inZone, avatar, parent)
|
||||||
inZone.Population.tell(Zone.Population.Leave(avatar), parent)
|
inZone.Population.tell(Zone.Population.Leave(avatar), parent)
|
||||||
|
log.info(s"logout of ${avatar.name}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,6 +419,13 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
||||||
private[this] case class Logout(name : String)
|
private[this] case class Logout(name : String)
|
||||||
|
|
||||||
object PlayerToken {
|
object PlayerToken {
|
||||||
|
object DeniedLoginReason extends Enumeration {
|
||||||
|
val
|
||||||
|
Denied, //generic
|
||||||
|
Kicked
|
||||||
|
= Value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message dispatched to confirm that a player with given locational attributes exists.
|
* Message dispatched to confirm that a player with given locational attributes exists.
|
||||||
* Agencies outside of the `AccountPersistanceService`/`PlayerToken` system make use of this message.
|
* Agencies outside of the `AccountPersistanceService`/`PlayerToken` system make use of this message.
|
||||||
|
|
@ -356,4 +435,6 @@ object PlayerToken {
|
||||||
* @param position where in the zone the player is located
|
* @param position where in the zone the player is located
|
||||||
*/
|
*/
|
||||||
final case class LoginInfo(name : String, zone : Zone, position : Vector3)
|
final case class LoginInfo(name : String, zone : Zone, position : Vector3)
|
||||||
|
|
||||||
|
final case class CanNotLogin(name : String, reason : DeniedLoginReason.Value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1092,7 +1092,7 @@ class WorldSessionActor extends Actor
|
||||||
case msg@Zoning.InstantAction.Located(zone, _, spawn_point) =>
|
case msg@Zoning.InstantAction.Located(zone, _, spawn_point) =>
|
||||||
//in between subsequent reply messages, it does not matter if the destination changes
|
//in between subsequent reply messages, it does not matter if the destination changes
|
||||||
//so long as there is at least one destination at all (including the fallback)
|
//so long as there is at least one destination at all (including the fallback)
|
||||||
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction))) {
|
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction), cluster)) {
|
||||||
val (pos, ori) = spawn_point.SpecificPoint(player)
|
val (pos, ori) = spawn_point.SpecificPoint(player)
|
||||||
SpawnThroughZoningProcess(zone, pos, ori)
|
SpawnThroughZoningProcess(zone, pos, ori)
|
||||||
}
|
}
|
||||||
|
|
@ -1103,7 +1103,7 @@ class WorldSessionActor extends Actor
|
||||||
case Zoning.InstantAction.NotLocated() =>
|
case Zoning.InstantAction.NotLocated() =>
|
||||||
instantActionFallbackDestination match {
|
instantActionFallbackDestination match {
|
||||||
case Some(Zoning.InstantAction.Located(zone, _, spawn_point)) if spawn_point.Owner.Faction == player.Faction && !spawn_point.Offline =>
|
case Some(Zoning.InstantAction.Located(zone, _, spawn_point)) if spawn_point.Owner.Faction == player.Faction && !spawn_point.Offline =>
|
||||||
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction))) {
|
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction), cluster)) {
|
||||||
val (pos, ori) = spawn_point.SpecificPoint(player)
|
val (pos, ori) = spawn_point.SpecificPoint(player)
|
||||||
SpawnThroughZoningProcess(zone, pos, ori)
|
SpawnThroughZoningProcess(zone, pos, ori)
|
||||||
}
|
}
|
||||||
|
|
@ -1116,7 +1116,7 @@ class WorldSessionActor extends Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
case Zoning.Recall.Located(zone, spawn_point) =>
|
case Zoning.Recall.Located(zone, spawn_point) =>
|
||||||
if(ContemplateZoningResponse(Zoning.Recall.Request(player.Faction, zone.Id))) {
|
if(ContemplateZoningResponse(Zoning.Recall.Request(player.Faction, zone.Id), cluster)) {
|
||||||
val (pos, ori) = spawn_point.SpecificPoint(player)
|
val (pos, ori) = spawn_point.SpecificPoint(player)
|
||||||
SpawnThroughZoningProcess(zone, pos, ori)
|
SpawnThroughZoningProcess(zone, pos, ori)
|
||||||
}
|
}
|
||||||
|
|
@ -1124,6 +1124,12 @@ class WorldSessionActor extends Actor
|
||||||
case Zoning.Recall.Denied(reason) =>
|
case Zoning.Recall.Denied(reason) =>
|
||||||
CancelZoningProcessWithReason(s"@norecall_sanctuary_$reason", Some(ChatMessageType.CMT_QUIT))
|
CancelZoningProcessWithReason(s"@norecall_sanctuary_$reason", Some(ChatMessageType.CMT_QUIT))
|
||||||
|
|
||||||
|
case Zoning.Quit() =>
|
||||||
|
if(ContemplateZoningResponse(Zoning.Quit(), self)) {
|
||||||
|
log.info("Good-bye")
|
||||||
|
ImmediateDisconnect()
|
||||||
|
}
|
||||||
|
|
||||||
case ZoningReset() =>
|
case ZoningReset() =>
|
||||||
CancelZoningProcess()
|
CancelZoningProcess()
|
||||||
|
|
||||||
|
|
@ -1375,6 +1381,10 @@ class WorldSessionActor extends Actor
|
||||||
inZone.AvatarEvents ! AvatarServiceMessage(playerName, AvatarAction.TeardownConnection())
|
inZone.AvatarEvents ! AvatarServiceMessage(playerName, AvatarAction.TeardownConnection())
|
||||||
//find and reload previous player
|
//find and reload previous player
|
||||||
(inZone.Players.find(p => p.name.equals(playerName)), inZone.LivePlayers.find(p => p.Name.equals(playerName))) match {
|
(inZone.Players.find(p => p.name.equals(playerName)), inZone.LivePlayers.find(p => p.Name.equals(playerName))) match {
|
||||||
|
case (_, Some(p)) if p.death_by == -1 =>
|
||||||
|
//player is not allowed
|
||||||
|
KickedByAdministration()
|
||||||
|
|
||||||
case (Some(a), Some(p)) if p.isAlive =>
|
case (Some(a), Some(p)) if p.isAlive =>
|
||||||
//rejoin current avatar/player
|
//rejoin current avatar/player
|
||||||
log.info(s"LoginInfo: player $playerName is alive")
|
log.info(s"LoginInfo: player $playerName is alive")
|
||||||
|
|
@ -1415,10 +1425,17 @@ class WorldSessionActor extends Actor
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
//fall back to sanctuary/prior?
|
//fall back to sanctuary/prior?
|
||||||
log.error(s"LoginInfo: player ${player.Name}Name could not be found in game world")
|
log.error(s"LoginInfo: player $playerName could not be found in game world")
|
||||||
self ! PlayerToken.LoginInfo(playerName, Zone.Nowhere, pos)
|
self ! PlayerToken.LoginInfo(playerName, Zone.Nowhere, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case PlayerToken.CanNotLogin(playerName, reason) =>
|
||||||
|
log.warn(s"LoginInfo: player $playerName is denied login for reason: $reason")
|
||||||
|
reason match {
|
||||||
|
case PlayerToken.DeniedLoginReason.Kicked => KickedByAdministration()
|
||||||
|
case _ => sendResponse(DisconnectMessage("You will be logged out."))
|
||||||
|
}
|
||||||
|
|
||||||
case msg @ Containable.ItemPutInSlot(_ : PlanetSideServerObject with Container, _ : Equipment, _ : Int, _ : Option[Equipment]) =>
|
case msg @ Containable.ItemPutInSlot(_ : PlanetSideServerObject with Container, _ : Equipment, _ : Int, _ : Option[Equipment]) =>
|
||||||
log.info(s"$msg")
|
log.info(s"$msg")
|
||||||
|
|
||||||
|
|
@ -1506,7 +1523,7 @@ class WorldSessionActor extends Actor
|
||||||
* @return `true`, if the zoning transportation process should start;
|
* @return `true`, if the zoning transportation process should start;
|
||||||
* `false`, otherwise
|
* `false`, otherwise
|
||||||
*/
|
*/
|
||||||
def ContemplateZoningResponse(nextStepMsg : Any) : Boolean = {
|
def ContemplateZoningResponse(nextStepMsg : Any, to : ActorRef) : Boolean = {
|
||||||
val descriptor = zoningType.toString.toLowerCase
|
val descriptor = zoningType.toString.toLowerCase
|
||||||
if(zoningStatus == Zoning.Status.Request) {
|
if(zoningStatus == Zoning.Status.Request) {
|
||||||
DeactivateImplants()
|
DeactivateImplants()
|
||||||
|
|
@ -1518,7 +1535,7 @@ class WorldSessionActor extends Actor
|
||||||
zoningReset.cancel
|
zoningReset.cancel
|
||||||
zoningTimer.cancel
|
zoningTimer.cancel
|
||||||
zoningReset = context.system.scheduler.scheduleOnce(10 seconds, self, ZoningReset())
|
zoningReset = context.system.scheduler.scheduleOnce(10 seconds, self, ZoningReset())
|
||||||
zoningTimer = context.system.scheduler.scheduleOnce(5 seconds, cluster, nextStepMsg)
|
zoningTimer = context.system.scheduler.scheduleOnce(5 seconds, to, nextStepMsg)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
else if(zoningStatus == Zoning.Status.Countdown) {
|
else if(zoningStatus == Zoning.Status.Countdown) {
|
||||||
|
|
@ -1531,7 +1548,7 @@ class WorldSessionActor extends Actor
|
||||||
}
|
}
|
||||||
//again
|
//again
|
||||||
zoningReset = context.system.scheduler.scheduleOnce(10 seconds, self, ZoningReset())
|
zoningReset = context.system.scheduler.scheduleOnce(10 seconds, self, ZoningReset())
|
||||||
zoningTimer = context.system.scheduler.scheduleOnce(5 seconds, cluster, nextStepMsg)
|
zoningTimer = context.system.scheduler.scheduleOnce(5 seconds, to, nextStepMsg)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1833,8 +1850,14 @@ class WorldSessionActor extends Actor
|
||||||
log.warn(s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server")
|
log.warn(s"KillPlayer/SHOTS_WHILE_DEAD: client of ${avatar.name} fired $shotsWhileDead rounds while character was dead on server")
|
||||||
shotsWhileDead = 0
|
shotsWhileDead = 0
|
||||||
}
|
}
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
reviveTimer.cancel
|
||||||
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(player.Faction), player, 7))
|
if(player.death_by == 0) {
|
||||||
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
|
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(player.Faction), player, 7))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
HandleReleaseAvatar(player, continent)
|
||||||
|
}
|
||||||
|
|
||||||
case AvatarResponse.LoadPlayer(pkt) =>
|
case AvatarResponse.LoadPlayer(pkt) =>
|
||||||
if(tplayer_guid != guid) {
|
if(tplayer_guid != guid) {
|
||||||
|
|
@ -2781,14 +2804,8 @@ class WorldSessionActor extends Actor
|
||||||
if(player.VehicleSeated.contains(vehicle_guid)) {
|
if(player.VehicleSeated.contains(vehicle_guid)) {
|
||||||
player.Position = pos
|
player.Position = pos
|
||||||
GetVehicleAndSeat() match {
|
GetVehicleAndSeat() match {
|
||||||
case (Some(_), Some(0)) => ;
|
case (Some(_), Some(seatNum)) if seatNum > 0 =>
|
||||||
case (Some(_), Some(_)) =>
|
|
||||||
turnCounter(guid)
|
turnCounter(guid)
|
||||||
if (player.death_by == -1) {
|
|
||||||
sendResponse(ChatMsg(ChatMessageType.UNK_71, true, "", "Your account has been logged out by a Customer Service Representative.", None))
|
|
||||||
Thread.sleep(300)
|
|
||||||
sendResponse(DropSession(sessionId, "kick by GM"))
|
|
||||||
}
|
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3873,9 +3890,7 @@ class WorldSessionActor extends Actor
|
||||||
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, player.Position, player.Velocity, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, player.spectator, wepInHand))
|
continent.AvatarEvents ! AvatarServiceMessage(continent.Id, AvatarAction.PlayerState(avatar_guid, player.Position, player.Velocity, yaw, pitch, yaw_upper, seq_time, is_crouching, is_jumping, jump_thrust, is_cloaking, player.spectator, wepInHand))
|
||||||
updateSquad()
|
updateSquad()
|
||||||
if(player.death_by == -1) {
|
if(player.death_by == -1) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.UNK_71, true, "", "Your account has been logged out by a Customer Service Representative.", None))
|
KickedByAdministration()
|
||||||
Thread.sleep(300)
|
|
||||||
sendResponse(DropSession(sessionId, "kick by GM"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg@ChildObjectStateMessage(object_guid, pitch, yaw) =>
|
case msg@ChildObjectStateMessage(object_guid, pitch, yaw) =>
|
||||||
|
|
@ -3906,9 +3921,7 @@ class WorldSessionActor extends Actor
|
||||||
//log.warn(s"ChildObjectState: player ${player.Name} not related to anything with a controllable agent")
|
//log.warn(s"ChildObjectState: player ${player.Name} not related to anything with a controllable agent")
|
||||||
}
|
}
|
||||||
if (player.death_by == -1) {
|
if (player.death_by == -1) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.UNK_71, true, "", "Your account has been logged out by a Customer Service Representative.", None))
|
KickedByAdministration()
|
||||||
Thread.sleep(300)
|
|
||||||
sendResponse(DropSession(sessionId, "kick by GM"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg@VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, flying, unk6, unk7, wheels, is_decelerating, is_cloaked) =>
|
case msg@VehicleStateMessage(vehicle_guid, unk1, pos, ang, vel, flying, unk6, unk7, wheels, is_decelerating, is_cloaked) =>
|
||||||
|
|
@ -3954,9 +3967,7 @@ class WorldSessionActor extends Actor
|
||||||
case _ => ;
|
case _ => ;
|
||||||
}
|
}
|
||||||
if (player.death_by == -1) {
|
if (player.death_by == -1) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.UNK_71, true, "", "Your account has been logged out by a Customer Service Representative.", None))
|
KickedByAdministration()
|
||||||
Thread.sleep(300)
|
|
||||||
sendResponse(DropSession(sessionId, "kick by GM"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case msg@VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
|
case msg@VehicleSubStateMessage(vehicle_guid, player_guid, vehicle_pos, vehicle_ang, vel, unk1, unk2) =>
|
||||||
|
|
@ -4041,18 +4052,26 @@ class WorldSessionActor extends Actor
|
||||||
else if(messagetype == ChatMessageType.CMT_RECALL) {
|
else if(messagetype == ChatMessageType.CMT_RECALL) {
|
||||||
makeReply = false
|
makeReply = false
|
||||||
val sanctuary = Zones.SanctuaryZoneId(player.Faction)
|
val sanctuary = Zones.SanctuaryZoneId(player.Faction)
|
||||||
if(zoningType == Zoning.Method.InstantAction) {
|
if(zoningType == Zoning.Method.Quit) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_instantactionting", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall to your sanctuary continent while quitting", None))
|
||||||
|
}
|
||||||
|
else if(zoningType == Zoning.Method.InstantAction) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall to your sanctuary continent while instant actioning", None))
|
||||||
}
|
}
|
||||||
else if(zoningType == Zoning.Method.Recall) {
|
else if(zoningType == Zoning.Method.Recall) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You already requested to recall to your sanctuary continent.", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You already requested to recall to your sanctuary continent", None))
|
||||||
}
|
}
|
||||||
else if(continent.Id.equals(sanctuary)) {
|
else if(continent.Id.equals(sanctuary)) {
|
||||||
//nonstandard message
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall to your sanctuary continent when you are already on your faction's sanctuary continent", None))
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall when you are already on your faction's sanctuary continent.", None))
|
|
||||||
}
|
}
|
||||||
else if(deadState != DeadState.Alive) {
|
else if(!player.isAlive || deadState != DeadState.Alive) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_dead", None))
|
if (player.isAlive) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_deconstructing", None))
|
||||||
|
//sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall to your sanctuary continent while deconstructing.", None))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_dead", None))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(player.VehicleSeated.nonEmpty) {
|
else if(player.VehicleSeated.nonEmpty) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_invehicle", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_invehicle", None))
|
||||||
|
|
@ -4067,15 +4086,22 @@ class WorldSessionActor extends Actor
|
||||||
}
|
}
|
||||||
else if(messagetype == ChatMessageType.CMT_INSTANTACTION) {
|
else if(messagetype == ChatMessageType.CMT_INSTANTACTION) {
|
||||||
makeReply = false
|
makeReply = false
|
||||||
if(zoningType == Zoning.Method.InstantAction) {
|
if(zoningType == Zoning.Method.Quit) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't instant action while quitting.", None))
|
||||||
|
}
|
||||||
|
else if(zoningType == Zoning.Method.InstantAction) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_instantactionting", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_instantactionting", None))
|
||||||
}
|
}
|
||||||
else if(zoningType == Zoning.Method.Recall) {
|
else if(zoningType == Zoning.Method.Recall) {
|
||||||
//nonstandard message
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You won't instant action. You already requested to recall to your sanctuary continent", None))
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You already requested to recall to your sanctuary continent.", None))
|
|
||||||
}
|
}
|
||||||
else if(deadState != DeadState.Alive) {
|
else if(!player.isAlive || deadState != DeadState.Alive) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_dead", None))
|
if(player.isAlive) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_deconstructing", None))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_dead", None))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(player.VehicleSeated.nonEmpty) {
|
else if(player.VehicleSeated.nonEmpty) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_invehicle", None))
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_invehicle", None))
|
||||||
|
|
@ -4088,6 +4114,33 @@ class WorldSessionActor extends Actor
|
||||||
cluster ! Zoning.InstantAction.Request(player.Faction)
|
cluster ! Zoning.InstantAction.Request(player.Faction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(messagetype == ChatMessageType.CMT_QUIT) {
|
||||||
|
makeReply = false
|
||||||
|
if(zoningType == Zoning.Method.Quit) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noquit_quitting", None))
|
||||||
|
}
|
||||||
|
else if(!player.isAlive || deadState != DeadState.Alive) {
|
||||||
|
if(player.isAlive) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noquit_deconstructing", None))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noquit_dead", None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(player.VehicleSeated.nonEmpty) {
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noquit_invehicle", None))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//priority to quitting is given to quit over other zoning methods
|
||||||
|
if(zoningType == Zoning.Method.InstantAction || zoningType == Zoning.Method.Recall) {
|
||||||
|
CancelZoningProcessWithDescriptiveReason("cancel")
|
||||||
|
}
|
||||||
|
zoningType = Zoning.Method.Quit
|
||||||
|
zoningChatMessageType = messagetype
|
||||||
|
zoningStatus = Zoning.Status.Request
|
||||||
|
self ! Zoning.Quit()
|
||||||
|
}
|
||||||
|
}
|
||||||
CSRZone.read(traveler, msg) match {
|
CSRZone.read(traveler, msg) match {
|
||||||
case (true, zone, pos) =>
|
case (true, zone, pos) =>
|
||||||
if (player.isAlive && zone != player.Continent && (admin || zone == "z8" || zone == "c1" || zone == "c2" || zone == "c3" || zone == "c4" || zone == "c5" || zone == "c6" ||
|
if (player.isAlive && zone != player.Continent && (admin || zone == "z8" || zone == "c1" || zone == "c2" || zone == "c3" || zone == "c4" || zone == "c5" || zone == "c6" ||
|
||||||
|
|
@ -4174,9 +4227,6 @@ class WorldSessionActor extends Actor
|
||||||
case _ =>
|
case _ =>
|
||||||
self ! PacketCoding.CreateGamePacket(0, RequestDestroyMessage(PlanetSideGUID(guid)))
|
self ! PacketCoding.CreateGamePacket(0, RequestDestroyMessage(PlanetSideGUID(guid)))
|
||||||
}
|
}
|
||||||
} else if(messagetype == ChatMessageType.CMT_QUIT) { // TODO: handle this appropriately
|
|
||||||
sendResponse(DropCryptoSession())
|
|
||||||
sendResponse(DropSession(sessionId, "user quit"))
|
|
||||||
}
|
}
|
||||||
//dev hack; consider bang-commands to complement slash-commands in future
|
//dev hack; consider bang-commands to complement slash-commands in future
|
||||||
if(trimContents.equals("!loc")) {
|
if(trimContents.equals("!loc")) {
|
||||||
|
|
@ -4351,25 +4401,26 @@ class WorldSessionActor extends Actor
|
||||||
// StopBundlingPackets()
|
// StopBundlingPackets()
|
||||||
}
|
}
|
||||||
else if (trimContents.contains("!kick") && admin) {
|
else if (trimContents.contains("!kick") && admin) {
|
||||||
val CharIDorName : String = contents.drop(contents.indexOf(" ") + 1)
|
val input = trimContents.split("\\s+").drop(1)
|
||||||
try {
|
if(input.length > 0) {
|
||||||
val charID : Long = CharIDorName.toLong
|
val numRegex = raw"(\d+)".r
|
||||||
if(charID != player.CharId) {
|
val id = input(0)
|
||||||
var charToKick = continent.LivePlayers.filter(_.CharId == charID)
|
val determination : Player=>Boolean = id match {
|
||||||
if (charToKick.nonEmpty) {
|
case numRegex(_) => { _.CharId == id.toLong }
|
||||||
charToKick.head.death_by = -1
|
case _ => { _.Name.equals(id) }
|
||||||
}
|
|
||||||
else {
|
|
||||||
charToKick = continent.Corpses.filter(_.CharId == charID)
|
|
||||||
if (charToKick.nonEmpty) charToKick.head.death_by = -1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
continent.LivePlayers.find(determination).orElse(continent.Corpses.find(determination)) match {
|
||||||
catch {
|
case Some(tplayer) if AdministrativeKick(tplayer) =>
|
||||||
case _ : Throwable =>
|
if(input.length > 1) {
|
||||||
{
|
val time = input(1)
|
||||||
val charToKick = continent.LivePlayers.filter(_.Name.equalsIgnoreCase(CharIDorName))
|
time match {
|
||||||
if(charToKick.nonEmpty) charToKick.head.death_by = -1
|
case numRegex(_) =>
|
||||||
|
accountPersistence ! AccountPersistenceService.Kick(tplayer.Name, Some(time.toLong))
|
||||||
|
case _ =>
|
||||||
|
accountPersistence ! AccountPersistenceService.Kick(tplayer.Name, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5011,7 +5062,8 @@ class WorldSessionActor extends Actor
|
||||||
val lastUse = player.GetLastUsedTime(kid)
|
val lastUse = player.GetLastUsedTime(kid)
|
||||||
val delay = delayedGratificationEntries.getOrElse(kid, 0L)
|
val delay = delayedGratificationEntries.getOrElse(kid, 0L)
|
||||||
if((time - lastUse) < delay) {
|
if((time - lastUse) < delay) {
|
||||||
sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^${((delay / 1000) - math.ceil((time - lastUse).toDouble) / 1000)}~", None))
|
val displayedDelay = math.min(5, ((delay.toDouble / 1000) - math.ceil((time - lastUse).toDouble) / 1000) + 1).toInt
|
||||||
|
sendResponse(ChatMsg(ChatMessageType.UNK_225, false, "", s"@TimeUntilNextUse^$displayedDelay~", None))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val indexOpt = player.Find(kit)
|
val indexOpt = player.Find(kit)
|
||||||
|
|
@ -7616,6 +7668,7 @@ class WorldSessionActor extends Actor
|
||||||
val obj = Player.Respawn(tplayer)
|
val obj = Player.Respawn(tplayer)
|
||||||
obj.ResetAllImplants()
|
obj.ResetAllImplants()
|
||||||
LoadClassicDefault(obj)
|
LoadClassicDefault(obj)
|
||||||
|
obj.death_by = tplayer.death_by
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7688,7 +7741,15 @@ class WorldSessionActor extends Actor
|
||||||
/**
|
/**
|
||||||
* Creates a player that has the characteristics of a corpse.
|
* Creates a player that has the characteristics of a corpse.
|
||||||
* To the game, that is a backpack (or some pastry, festive graphical modification allowing).
|
* To the game, that is a backpack (or some pastry, festive graphical modification allowing).
|
||||||
|
* A player who has been kicked may not turn into a corpse.
|
||||||
|
* @see `AvatarAction.Release`
|
||||||
|
* @see `AvatarServiceMessage`
|
||||||
* @see `CorpseConverter.converter`
|
* @see `CorpseConverter.converter`
|
||||||
|
* @see `DepictPlayerAsCorpse`
|
||||||
|
* @see `Player.Release`
|
||||||
|
* @see `Zone.AvatarEvents`
|
||||||
|
* @see `Zone.Corpse.Add`
|
||||||
|
* @see `Zone.Population`
|
||||||
* @param tplayer the player
|
* @param tplayer the player
|
||||||
*/
|
*/
|
||||||
def TurnPlayerIntoCorpse(tplayer : Player, zone : Zone) : Unit = {
|
def TurnPlayerIntoCorpse(tplayer : Player, zone : Zone) : Unit = {
|
||||||
|
|
@ -7729,7 +7790,7 @@ class WorldSessionActor extends Actor
|
||||||
*/
|
*/
|
||||||
def TryDisposeOfLootedCorpse(obj : Player) : Boolean = {
|
def TryDisposeOfLootedCorpse(obj : Player) : Boolean = {
|
||||||
if(obj.isBackpack && WellLootedDeadBody(obj)) {
|
if(obj.isBackpack && WellLootedDeadBody(obj)) {
|
||||||
continent.AvatarEvents ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), continent))
|
obj.Zone.AvatarEvents ! AvatarServiceMessage.Corpse(RemoverActor.HurrySpecific(List(obj), obj.Zone))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -8149,7 +8210,7 @@ class WorldSessionActor extends Actor
|
||||||
target match {
|
target match {
|
||||||
case obj : Player if obj.CanDamage && obj.Actor != Default.Actor =>
|
case obj : Player if obj.CanDamage && obj.Actor != Default.Actor =>
|
||||||
if(obj.spectator) {
|
if(obj.spectator) {
|
||||||
player.death_by = -1 // little thing for auto kick
|
AdministrativeKick(player, obj != player) // little thing for auto kick
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
obj.Actor ! Vitality.Damage(func)
|
obj.Actor ! Vitality.Damage(func)
|
||||||
|
|
@ -10169,6 +10230,39 @@ class WorldSessionActor extends Actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def AdministrativeKick(tplayer : Player, permitKickSelf : Boolean = false) : Boolean = {
|
||||||
|
if(permitKickSelf || tplayer != player) { //stop kicking yourself
|
||||||
|
tplayer.death_by = -1
|
||||||
|
accountPersistence ! AccountPersistenceService.Kick(tplayer.Name)
|
||||||
|
//get out of that vehicle
|
||||||
|
GetMountableAndSeat(None, tplayer, continent) match {
|
||||||
|
case (Some(obj), Some(seatNum)) =>
|
||||||
|
tplayer.VehicleSeated = None
|
||||||
|
obj.Seats(seatNum).Occupant = None
|
||||||
|
continent.VehicleEvents ! VehicleServiceMessage(continent.Id, VehicleAction.KickPassenger(tplayer.GUID, seatNum, false, obj.GUID))
|
||||||
|
case _ => ;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def KickedByAdministration() : Unit = {
|
||||||
|
sendResponse(DisconnectMessage("Your account has been logged out by a Customer Service Representative."))
|
||||||
|
Thread.sleep(300)
|
||||||
|
sendResponse(DropSession(sessionId, "kick by GM"))
|
||||||
|
}
|
||||||
|
|
||||||
|
def ImmediateDisconnect() : Unit = {
|
||||||
|
if(avatar != null) {
|
||||||
|
accountPersistence ! AccountPersistenceService.Logout(avatar.name)
|
||||||
|
}
|
||||||
|
sendResponse(DropCryptoSession())
|
||||||
|
sendResponse(DropSession(sessionId, "user quit"))
|
||||||
|
}
|
||||||
|
|
||||||
def failWithError(error : String) = {
|
def failWithError(error : String) = {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
sendResponse(ConnectionClose())
|
sendResponse(ConnectionClose())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue