mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-01-20 02:24:45 +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
|
|
@ -10,7 +10,8 @@ object Zoning {
|
|||
val
|
||||
None,
|
||||
InstantAction,
|
||||
Recall
|
||||
Recall,
|
||||
Quit
|
||||
= Value
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +36,8 @@ object Zoning {
|
|||
final val Enemy = TimeType(30, "Enemy")
|
||||
}
|
||||
|
||||
final case class Quit()
|
||||
|
||||
object InstantAction {
|
||||
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>
|
||||
* 25 - message: you are no longer imprinted (updates imprinted status; 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>
|
||||
* 32 - switch to first person view<br>
|
||||
* 33 - event: fail to deconstruct<br>
|
||||
* 43 - prompt: friendly fire in virtual reality zone<br>
|
||||
* 45 - ?<br>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@ import net.psforever.objects._
|
|||
import net.psforever.objects.serverobject.mount.Mountable
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.types.Vector3
|
||||
import services.{RemoverActor, Service, ServiceManager}
|
||||
import services.{Service, ServiceManager}
|
||||
import services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import services.vehicle.VehicleServiceMessage
|
||||
|
||||
/**
|
||||
* 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) =>
|
||||
ref ! msg
|
||||
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)
|
||||
}
|
||||
|
||||
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?
|
||||
accounts.remove(target)
|
||||
|
||||
|
|
@ -114,7 +137,7 @@ class AccountPersistenceService extends Actor {
|
|||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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
|
||||
/** the last-reported game coordinate position of this player */
|
||||
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 */
|
||||
var timer : Cancellable = Default.Cancellable
|
||||
/** the sparingly-used log */
|
||||
|
|
@ -204,17 +249,44 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
|||
|
||||
def receive : Receive = {
|
||||
case AccountPersistenceService.Login(_) =>
|
||||
sender ! PlayerToken.LoginInfo(name, inZone, lastPosition)
|
||||
UpdateTimer()
|
||||
sender ! (if(kicked) {
|
||||
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
|
||||
lastPosition = p
|
||||
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(_) =>
|
||||
context.parent ! Logout(name)
|
||||
context.stop(self)
|
||||
kickTime match {
|
||||
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 _ => ;
|
||||
}
|
||||
|
|
@ -224,7 +296,7 @@ class PersistenceMonitor(name : String, squadService : ActorRef, taskResolver :
|
|||
*/
|
||||
def UpdateTimer() : Unit = {
|
||||
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.
|
||||
*/
|
||||
def PerformLogout() : Unit = {
|
||||
log.info(s"logout of $name")
|
||||
(inZone.Players.find(p => p.name == name), inZone.LivePlayers.find(p => p.Name == name)) match {
|
||||
case (Some(avatar), Some(player)) if player.VehicleSeated.nonEmpty =>
|
||||
//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)
|
||||
Deployables.Disown(inZone, 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)
|
||||
|
||||
object PlayerToken {
|
||||
object DeniedLoginReason extends Enumeration {
|
||||
val
|
||||
Denied, //generic
|
||||
Kicked
|
||||
= Value
|
||||
}
|
||||
|
||||
/**
|
||||
* Message dispatched to confirm that a player with given locational attributes exists.
|
||||
* 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
|
||||
*/
|
||||
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) =>
|
||||
//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)
|
||||
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction))) {
|
||||
if(ContemplateZoningResponse(Zoning.InstantAction.Request(player.Faction), cluster)) {
|
||||
val (pos, ori) = spawn_point.SpecificPoint(player)
|
||||
SpawnThroughZoningProcess(zone, pos, ori)
|
||||
}
|
||||
|
|
@ -1103,7 +1103,7 @@ class WorldSessionActor extends Actor
|
|||
case Zoning.InstantAction.NotLocated() =>
|
||||
instantActionFallbackDestination match {
|
||||
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)
|
||||
SpawnThroughZoningProcess(zone, pos, ori)
|
||||
}
|
||||
|
|
@ -1116,7 +1116,7 @@ class WorldSessionActor extends Actor
|
|||
}
|
||||
|
||||
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)
|
||||
SpawnThroughZoningProcess(zone, pos, ori)
|
||||
}
|
||||
|
|
@ -1124,6 +1124,12 @@ class WorldSessionActor extends Actor
|
|||
case Zoning.Recall.Denied(reason) =>
|
||||
CancelZoningProcessWithReason(s"@norecall_sanctuary_$reason", Some(ChatMessageType.CMT_QUIT))
|
||||
|
||||
case Zoning.Quit() =>
|
||||
if(ContemplateZoningResponse(Zoning.Quit(), self)) {
|
||||
log.info("Good-bye")
|
||||
ImmediateDisconnect()
|
||||
}
|
||||
|
||||
case ZoningReset() =>
|
||||
CancelZoningProcess()
|
||||
|
||||
|
|
@ -1375,6 +1381,10 @@ class WorldSessionActor extends Actor
|
|||
inZone.AvatarEvents ! AvatarServiceMessage(playerName, AvatarAction.TeardownConnection())
|
||||
//find and reload previous player
|
||||
(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 =>
|
||||
//rejoin current avatar/player
|
||||
log.info(s"LoginInfo: player $playerName is alive")
|
||||
|
|
@ -1415,10 +1425,17 @@ class WorldSessionActor extends Actor
|
|||
|
||||
case _ =>
|
||||
//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)
|
||||
}
|
||||
|
||||
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]) =>
|
||||
log.info(s"$msg")
|
||||
|
||||
|
|
@ -1506,7 +1523,7 @@ class WorldSessionActor extends Actor
|
|||
* @return `true`, if the zoning transportation process should start;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def ContemplateZoningResponse(nextStepMsg : Any) : Boolean = {
|
||||
def ContemplateZoningResponse(nextStepMsg : Any, to : ActorRef) : Boolean = {
|
||||
val descriptor = zoningType.toString.toLowerCase
|
||||
if(zoningStatus == Zoning.Status.Request) {
|
||||
DeactivateImplants()
|
||||
|
|
@ -1518,7 +1535,7 @@ class WorldSessionActor extends Actor
|
|||
zoningReset.cancel
|
||||
zoningTimer.cancel
|
||||
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
|
||||
}
|
||||
else if(zoningStatus == Zoning.Status.Countdown) {
|
||||
|
|
@ -1531,7 +1548,7 @@ class WorldSessionActor extends Actor
|
|||
}
|
||||
//again
|
||||
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
|
||||
}
|
||||
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")
|
||||
shotsWhileDead = 0
|
||||
}
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
reviveTimer = context.system.scheduler.scheduleOnce(respawnTimer milliseconds, cluster, Zone.Lattice.RequestSpawnPoint(Zones.SanctuaryZoneNumber(player.Faction), player, 7))
|
||||
reviveTimer.cancel
|
||||
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) =>
|
||||
if(tplayer_guid != guid) {
|
||||
|
|
@ -2781,14 +2804,8 @@ class WorldSessionActor extends Actor
|
|||
if(player.VehicleSeated.contains(vehicle_guid)) {
|
||||
player.Position = pos
|
||||
GetVehicleAndSeat() match {
|
||||
case (Some(_), Some(0)) => ;
|
||||
case (Some(_), Some(_)) =>
|
||||
case (Some(_), Some(seatNum)) if seatNum > 0 =>
|
||||
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 _ => ;
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
updateSquad()
|
||||
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"))
|
||||
KickedByAdministration()
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
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"))
|
||||
KickedByAdministration()
|
||||
}
|
||||
|
||||
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 _ => ;
|
||||
}
|
||||
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"))
|
||||
KickedByAdministration()
|
||||
}
|
||||
|
||||
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) {
|
||||
makeReply = false
|
||||
val sanctuary = Zones.SanctuaryZoneId(player.Faction)
|
||||
if(zoningType == Zoning.Method.InstantAction) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_instantactionting", None))
|
||||
if(zoningType == Zoning.Method.Quit) {
|
||||
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) {
|
||||
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)) {
|
||||
//nonstandard message
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You can't recall when you are already on your faction's sanctuary continent.", None))
|
||||
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))
|
||||
}
|
||||
else if(deadState != DeadState.Alive) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_dead", None))
|
||||
else if(!player.isAlive || deadState != DeadState.Alive) {
|
||||
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) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@norecall_invehicle", None))
|
||||
|
|
@ -4067,15 +4086,22 @@ class WorldSessionActor extends Actor
|
|||
}
|
||||
else if(messagetype == ChatMessageType.CMT_INSTANTACTION) {
|
||||
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))
|
||||
}
|
||||
else if(zoningType == Zoning.Method.Recall) {
|
||||
//nonstandard message
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You already requested to recall to your sanctuary continent.", None))
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "You won't instant action. You already requested to recall to your sanctuary continent", None))
|
||||
}
|
||||
else if(deadState != DeadState.Alive) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_dead", None))
|
||||
else if(!player.isAlive || deadState != DeadState.Alive) {
|
||||
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) {
|
||||
sendResponse(ChatMsg(ChatMessageType.CMT_QUIT, false, "", "@noinstantaction_invehicle", None))
|
||||
|
|
@ -4088,6 +4114,33 @@ class WorldSessionActor extends Actor
|
|||
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 {
|
||||
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" ||
|
||||
|
|
@ -4174,9 +4227,6 @@ class WorldSessionActor extends Actor
|
|||
case _ =>
|
||||
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
|
||||
if(trimContents.equals("!loc")) {
|
||||
|
|
@ -4351,25 +4401,26 @@ class WorldSessionActor extends Actor
|
|||
// StopBundlingPackets()
|
||||
}
|
||||
else if (trimContents.contains("!kick") && admin) {
|
||||
val CharIDorName : String = contents.drop(contents.indexOf(" ") + 1)
|
||||
try {
|
||||
val charID : Long = CharIDorName.toLong
|
||||
if(charID != player.CharId) {
|
||||
var charToKick = continent.LivePlayers.filter(_.CharId == charID)
|
||||
if (charToKick.nonEmpty) {
|
||||
charToKick.head.death_by = -1
|
||||
}
|
||||
else {
|
||||
charToKick = continent.Corpses.filter(_.CharId == charID)
|
||||
if (charToKick.nonEmpty) charToKick.head.death_by = -1
|
||||
}
|
||||
val input = trimContents.split("\\s+").drop(1)
|
||||
if(input.length > 0) {
|
||||
val numRegex = raw"(\d+)".r
|
||||
val id = input(0)
|
||||
val determination : Player=>Boolean = id match {
|
||||
case numRegex(_) => { _.CharId == id.toLong }
|
||||
case _ => { _.Name.equals(id) }
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Throwable =>
|
||||
{
|
||||
val charToKick = continent.LivePlayers.filter(_.Name.equalsIgnoreCase(CharIDorName))
|
||||
if(charToKick.nonEmpty) charToKick.head.death_by = -1
|
||||
continent.LivePlayers.find(determination).orElse(continent.Corpses.find(determination)) match {
|
||||
case Some(tplayer) if AdministrativeKick(tplayer) =>
|
||||
if(input.length > 1) {
|
||||
val time = input(1)
|
||||
time match {
|
||||
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 delay = delayedGratificationEntries.getOrElse(kid, 0L)
|
||||
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 {
|
||||
val indexOpt = player.Find(kit)
|
||||
|
|
@ -7616,6 +7668,7 @@ class WorldSessionActor extends Actor
|
|||
val obj = Player.Respawn(tplayer)
|
||||
obj.ResetAllImplants()
|
||||
LoadClassicDefault(obj)
|
||||
obj.death_by = tplayer.death_by
|
||||
obj
|
||||
}
|
||||
|
||||
|
|
@ -7688,7 +7741,15 @@ class WorldSessionActor extends Actor
|
|||
/**
|
||||
* Creates a player that has the characteristics of a corpse.
|
||||
* 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 `DepictPlayerAsCorpse`
|
||||
* @see `Player.Release`
|
||||
* @see `Zone.AvatarEvents`
|
||||
* @see `Zone.Corpse.Add`
|
||||
* @see `Zone.Population`
|
||||
* @param tplayer the player
|
||||
*/
|
||||
def TurnPlayerIntoCorpse(tplayer : Player, zone : Zone) : Unit = {
|
||||
|
|
@ -7729,7 +7790,7 @@ class WorldSessionActor extends Actor
|
|||
*/
|
||||
def TryDisposeOfLootedCorpse(obj : Player) : Boolean = {
|
||||
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
|
||||
}
|
||||
else {
|
||||
|
|
@ -8149,7 +8210,7 @@ class WorldSessionActor extends Actor
|
|||
target match {
|
||||
case obj : Player if obj.CanDamage && obj.Actor != Default.Actor =>
|
||||
if(obj.spectator) {
|
||||
player.death_by = -1 // little thing for auto kick
|
||||
AdministrativeKick(player, obj != player) // little thing for auto kick
|
||||
}
|
||||
else {
|
||||
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) = {
|
||||
log.error(error)
|
||||
sendResponse(ConnectionClose())
|
||||
|
|
|
|||
Loading…
Reference in a new issue