More fixes

* Fix !list not distinguishing between players and corpses
* Fix /worldkick not working
* /silence no longer responds with a message window
* Fix 96-99 zone map files
* Fix implants not resetting after being jammend
* Fix MAXes requiring the wrong certs
This commit is contained in:
Jakob Gillich 2020-08-27 23:04:05 +02:00
parent 8a0a7d8539
commit 08eed0f331
11 changed files with 28599 additions and 28587 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -168,7 +168,7 @@ object AvatarActor {
final case class DeactivateActiveImplants() extends Command final case class DeactivateActiveImplants() extends Command
/** Start implant initialization timers (after zoning or respawn) */ /** Start implant initialization timers (after zoning or respawn) */
final case class InitializeImplants(instant: Boolean = false) extends Command final case class InitializeImplants() extends Command
/** Deinitialize implants (before zoning or respawning) */ /** Deinitialize implants (before zoning or respawning) */
final case class DeinitializeImplants() extends Command final case class DeinitializeImplants() extends Command
@ -582,6 +582,7 @@ class AvatarActor(
) )
case _ => ; case _ => ;
} }
deinitializeImplants()
Behaviors.same Behaviors.same
case LearnImplant(terminalGuid, definition) => case LearnImplant(terminalGuid, definition) =>
@ -880,8 +881,8 @@ class AvatarActor(
) )
Behaviors.same Behaviors.same
case InitializeImplants(instant) => case InitializeImplants() =>
initializeImplants(instant) initializeImplants()
Behaviors.same Behaviors.same
case DeinitializeImplants() => case DeinitializeImplants() =>
@ -890,7 +891,7 @@ class AvatarActor(
case ResetImplants() => case ResetImplants() =>
deinitializeImplants() deinitializeImplants()
initializeImplants(instant = false) initializeImplants()
Behaviors.same Behaviors.same
case AwardBep(bep) => case AwardBep(bep) =>
@ -1026,7 +1027,7 @@ class AvatarActor(
consumed consumed
} }
def initializeImplants(instant: Boolean): Unit = { def initializeImplants(): Unit = {
avatar.implants.zipWithIndex.foreach { avatar.implants.zipWithIndex.foreach {
case (Some(implant), slot) => case (Some(implant), slot) =>
// 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
@ -1051,7 +1052,7 @@ class AvatarActor(
implantTimers.get(slot).foreach(_.cancel()) implantTimers.get(slot).foreach(_.cancel())
implantTimers(slot) = context.system.scheduler.scheduleOnce( implantTimers(slot) = context.system.scheduler.scheduleOnce(
if (instant) 0.seconds else implant.definition.InitializationDuration.seconds, implant.definition.InitializationDuration.seconds,
() => { () => {
avatar = avatar.copy(implants = avatar.implants.map { avatar = avatar.copy(implants = avatar.implants.map {
case Some(implant) => Some(implant.copy(initialized = true)) case Some(implant) => Some(implant.copy(initialized = true))
@ -1071,9 +1072,8 @@ class AvatarActor(
avatar = avatar.copy(implants = avatar.implants.zipWithIndex.map { avatar = avatar.copy(implants = avatar.implants.zipWithIndex.map {
case (Some(implant), slot) => case (Some(implant), slot) =>
if (implant.active) { if (implant.active) {
context.self ! DeactivateImplant(implant.definition.implantType) deactivateImplant(implant.definition.implantType)
} }
sessionActor ! SessionActor.SendResponse(ActionProgressMessage(slot + 6, 100))
session.get.zone.AvatarEvents ! AvatarServiceMessage( session.get.zone.AvatarEvents ! AvatarServiceMessage(
session.get.zone.id, session.get.zone.id,
AvatarAction.SendResponse( AvatarAction.SendResponse(

View file

@ -318,14 +318,15 @@ class ChatActor(
(zone.LivePlayers ++ zone.Corpses) (zone.LivePlayers ++ zone.Corpses)
.filter(_.CharId != session.player.CharId) .filter(_.CharId != session.player.CharId)
.sortBy(_.Name) .sortBy(p => (p.Name, !p.isAlive))
.foreach(player => { .foreach(player => {
val color = if (!player.isAlive) "\\#7" else ""
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ChatMsg( ChatMsg(
CMT_GMOPEN, CMT_GMOPEN,
message.wideContents, message.wideContents,
"Server", "Server",
s"\\#7${player.Name} (${player.Faction}) [${player.CharId}] at ${player.Position.x.toInt} ${player.Position.y.toInt} ${player.Position.z.toInt}", s"${color}${player.Name} (${player.Faction}) [${player.CharId}] at ${player.Position.x.toInt} ${player.Position.y.toInt} ${player.Position.z.toInt}",
message.note message.note
) )
) )
@ -824,45 +825,54 @@ class ChatActor(
} }
case (CMT_KICK, _, contents) if gmCommandAllowed => case (CMT_KICK, _, contents) if gmCommandAllowed =>
val input = contents.split("\\s+").drop(1) val inputs = contents.split("\\s+").filter(_ != "")
if (input.length > 0) { inputs.headOption match {
val numRegex = raw"(\d+)".r case Some(input) =>
val id = input(0) val determination: Player => Boolean = input.toLongOption match {
val determination: Player => Boolean = id match { case Some(id) => _.CharId == id
case numRegex(_) => _.CharId == id.toLong case _ => _.Name.equals(input)
case _ => _.Name.equals(id) }
} session.zone.LivePlayers
session.zone.LivePlayers .find(determination)
.find(determination) .orElse(session.zone.Corpses.find(determination)) match {
.orElse(session.zone.Corpses.find(determination)) match { case Some(player) =>
case Some(player) => inputs.lift(1).map(_.toLongOption) match {
input.lift(1) match { case Some(Some(time)) =>
case Some(numRegex(time)) => sessionActor ! SessionActor.Kick(player, Some(time))
sessionActor ! SessionActor.Kick(player, Some(time.toLong)) case _ =>
case _ => sessionActor ! SessionActor.Kick(player)
sessionActor ! SessionActor.Kick(player) }
}
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ChatMsg( ChatMsg(
CMT_GMOPEN, UNK_229,
message.wideContents, message.wideContents,
"Server", "Server",
"@kick_o", "@kick_i",
message.note message.note
)
) )
) case None =>
case None => sessionActor ! SessionActor.SendResponse(
sessionActor ! SessionActor.SendResponse( ChatMsg(
ChatMsg( UNK_229,
CMT_GMOPEN, message.wideContents,
message.wideContents, "Server",
"Server", "@kick_o",
"@kick_o", message.note
message.note )
) )
}
case None =>
sessionActor ! SessionActor.SendResponse(
ChatMsg(
UNK_229,
message.wideContents,
"Server",
"@kick_o",
message.note
) )
} )
} }
case _ => case _ =>
@ -874,7 +884,7 @@ class ChatActor(
message.messageType match { message.messageType match {
case CMT_TELL | U_CMT_TELLFROM | CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | UNK_45 | UNK_71 | case CMT_TELL | U_CMT_TELLFROM | CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | UNK_45 | UNK_71 |
CMT_NOTE | CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_TR | CMT_GMBROADCAST_VS | CMT_NOTE | CMT_GMBROADCAST | CMT_GMBROADCAST_NC | CMT_GMBROADCAST_TR | CMT_GMBROADCAST_VS |
CMT_GMBROADCASTPOPUP | CMT_GMTELL | U_CMT_GMTELLFROM | UNK_227 => CMT_GMBROADCASTPOPUP | CMT_GMTELL | U_CMT_GMTELLFROM | UNK_227 | UNK_229 =>
sessionActor ! SessionActor.SendResponse(message) sessionActor ! SessionActor.SendResponse(message)
case CMT_OPEN => case CMT_OPEN =>
if ( if (
@ -906,20 +916,20 @@ class ChatActor(
if (session.player.silenced) { if (session.player.silenced) {
sessionActor ! SessionActor.SetSilenced(false) sessionActor ! SessionActor.SetSilenced(false)
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ChatMsg(ChatMessageType.UNK_71, true, "", "@silence_off", None) ChatMsg(ChatMessageType.UNK_229, true, "", "@silence_off", None)
) )
if (!silenceTimer.isCancelled) silenceTimer.cancel() if (!silenceTimer.isCancelled) silenceTimer.cancel()
} else { } else {
sessionActor ! SessionActor.SetSilenced(true) sessionActor ! SessionActor.SetSilenced(true)
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ChatMsg(ChatMessageType.UNK_71, true, "", "@silence_on", None) ChatMsg(ChatMessageType.UNK_229, true, "", "@silence_on", None)
) )
silenceTimer = context.system.scheduler.scheduleOnce( silenceTimer = context.system.scheduler.scheduleOnce(
time minutes, time minutes,
() => { () => {
sessionActor ! SessionActor.SetSilenced(false) sessionActor ! SessionActor.SetSilenced(false)
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
ChatMsg(ChatMessageType.UNK_71, true, "", "@silence_timeout", None) ChatMsg(ChatMessageType.UNK_229, true, "", "@silence_timeout", None)
) )
} }
) )

View file

@ -1284,6 +1284,8 @@ class SessionActor extends Actor with MDCContextAware {
log.info(s"Player ${tplayer.Name} has been loaded") log.info(s"Player ${tplayer.Name} has been loaded")
tplayer.avatar = avatar tplayer.avatar = avatar
session = session.copy(player = tplayer) session = session.copy(player = tplayer)
avatarActor ! AvatarActor.CreateImplants()
avatarActor ! AvatarActor.InitializeImplants()
//LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar //LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar
val weaponsEnabled = val weaponsEnabled =
session.zone.map.name != "map11" && session.zone.map.name != "map12" && session.zone.map.name != "map13" session.zone.map.name != "map11" && session.zone.map.name != "map12" && session.zone.map.name != "map13"
@ -1309,8 +1311,6 @@ class SessionActor extends Actor with MDCContextAware {
keepAliveFunc = NormalKeepAlive keepAliveFunc = NormalKeepAlive
upstreamMessageCount = 0 upstreamMessageCount = 0
setAvatar = false setAvatar = false
avatarActor ! AvatarActor.CreateImplants()
avatarActor ! AvatarActor.InitializeImplants()
persist() persist()
case PlayerLoaded(tplayer) => case PlayerLoaded(tplayer) =>
@ -1337,24 +1337,24 @@ class SessionActor extends Actor with MDCContextAware {
failWithError(s"${tplayer.Name} failed to load anywhere") failWithError(s"${tplayer.Name} failed to load anywhere")
} }
/* /**
The user is either already in the current zone and merely transporting himself from one location to another, * The user is either already in the current zone and merely transporting himself from one location to another,
also called "dying", or occasionally "deconstructing," * also called "dying", or occasionally "deconstructing,"
or is completely switching in between zones. * or is completely switching in between zones.
These correspond to the message NewPlayerLoaded for the case of "dying" or the latter zone switching case, * These correspond to the message NewPlayerLoaded for the case of "dying" or the latter zone switching case,
and PlayerLoaded for "deconstruction." * and PlayerLoaded for "deconstruction."
In the latter case, the user must wait for the zone to be recognized as loaded for the server * In the latter case, the user must wait for the zone to be recognized as loaded for the server
and this is performed through the send LoadMapMessage, receive BeginZoningMessage exchange * and this is performed through the send LoadMapMessage, receive BeginZoningMessage exchange
The user's player should have already been registered into the new zone * The user's player should have already been registered into the new zone
and is at some stage of being added to the zone in which they will have control agency in that zone. * and is at some stage of being added to the zone in which they will have control agency in that zone.
Whether or not the zone is loaded in the earlier case depends on the destination with respect to the current location. * Whether or not the zone is loaded in the earlier case depends on the destination with respect to the current location.
Once all of the following is (assumed) accomplished, * Once all of the following is (assumed) accomplished,
the server will attempt to declare that user's player the avatar of the user's client. * the server will attempt to declare that user's player the avatar of the user's client.
Reception of certain packets that represent "reported user activity" after that marks the end of avatar loading. * Reception of certain packets that represent "reported user activity" after that marks the end of avatar loading.
If the maximum number of unsuccessful attempts is reached, some course of action is taken. * If the maximum number of unsuccessful attempts is reached, some course of action is taken.
If the player dies, the process does not need to continue. * If the player dies, the process does not need to continue.
He may or may not be accompanied by a vehicle at any stage of this process. * He may or may not be accompanied by a vehicle at any stage of this process.
*/ */
case SetCurrentAvatar(tplayer, max_attempts, attempt) => case SetCurrentAvatar(tplayer, max_attempts, attempt) =>
respawnTimer.cancel() respawnTimer.cancel()
val waitingOnUpstream = upstreamMessageCount == 0 val waitingOnUpstream = upstreamMessageCount == 0
@ -3064,6 +3064,7 @@ class SessionActor extends Actor with MDCContextAware {
player.Actor ! JammableUnit.ClearJammeredStatus() player.Actor ! JammableUnit.ClearJammeredStatus()
player.Actor ! JammableUnit.ClearJammeredSound() player.Actor ! JammableUnit.ClearJammeredSound()
} }
// TODO only when respawning after death
avatarActor ! AvatarActor.ResetImplants() avatarActor ! AvatarActor.ResetImplants()
sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0)) sendResponse(PlanetsideAttributeMessage(PlanetSideGUID(0), 82, 0))
@ -4507,7 +4508,8 @@ class SessionActor extends Actor with MDCContextAware {
} else { } else {
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType) avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
} }
case _ => log.error(s"AvatarImplantMessage for unknown or uninitialized implant ${msg}") case Some(implant) if !implant.initialized => ()
case _ => log.error(s"AvatarImplantMessage for unknown implant ${msg}")
} }
} }
@ -5683,7 +5685,7 @@ class SessionActor extends Actor with MDCContextAware {
log.info("Battleplan: " + msg) log.info("Battleplan: " + msg)
case msg @ CreateShortcutMessage(player_guid, slot, unk, add, shortcut) => case msg @ CreateShortcutMessage(player_guid, slot, unk, add, shortcut) =>
log.info("CreateShortcutMessage: " + msg) log.debug("CreateShortcutMessage: " + msg)
case msg @ FriendsRequest(action, friend) => case msg @ FriendsRequest(action, friend) =>
log.info("FriendsRequest: " + msg) log.info("FriendsRequest: " + msg)
@ -7116,10 +7118,10 @@ class SessionActor extends Actor with MDCContextAware {
*/ */
def RespawnClone(tplayer: Player): Player = { def RespawnClone(tplayer: Player): Player = {
// workaround to make sure player is spawned with full stamina // workaround to make sure player is spawned with full stamina
tplayer.avatar = tplayer.avatar.copy(stamina = avatar.maxStamina) player.avatar = player.avatar.copy(stamina = avatar.maxStamina)
val obj = Player.Respawn(tplayer)
avatarActor ! AvatarActor.RestoreStamina(avatar.maxStamina) avatarActor ! AvatarActor.RestoreStamina(avatar.maxStamina)
avatarActor ! AvatarActor.ResetImplants() avatarActor ! AvatarActor.ResetImplants()
val obj = Player.Respawn(tplayer)
DefinitionUtil.applyDefaultLoadout(obj) DefinitionUtil.applyDefaultLoadout(obj)
obj.death_by = tplayer.death_by obj.death_by = tplayer.death_by
obj obj
@ -8320,7 +8322,7 @@ class SessionActor extends Actor with MDCContextAware {
respawnTime.toMillis, respawnTime.toMillis,
Vector3.Zero, Vector3.Zero,
player.Faction, player.Faction,
true unk5 = true
) )
) )
shiftPosition = Some(pos) shiftPosition = Some(pos)

View file

@ -726,7 +726,7 @@ class PlayerControl(player: Player, avatarActor: typed.ActorRef[AvatarActor.Comm
} }
override def CancelJammeredStatus(target: Any): Unit = { override def CancelJammeredStatus(target: Any): Unit = {
avatarActor ! AvatarActor.InitializeImplants(instant = true) avatarActor ! AvatarActor.InitializeImplants()
super.CancelJammeredStatus(target) super.CancelJammeredStatus(target)
} }

View file

@ -115,9 +115,9 @@ object InfantryLoadout {
*/ */
def DetermineSubtypeC(subtype: Int): Set[Certification] = def DetermineSubtypeC(subtype: Int): Set[Certification] =
subtype match { subtype match {
case 1 => Set(Certification.AIMAX, Certification.UniMAX) case 1 => Set(Certification.AAMAX, Certification.UniMAX)
case 2 => Set(Certification.AVMAX, Certification.UniMAX) case 2 => Set(Certification.AIMAX, Certification.UniMAX)
case 3 => Set(Certification.AAMAX, Certification.UniMAX) case 3 => Set(Certification.AVMAX, Certification.UniMAX)
case _ => Set.empty[Certification] case _ => Set.empty[Certification]
} }
} }

View file

@ -164,30 +164,30 @@ case object MapInfo extends StringEnum[MapInfo] {
scale = MapScale.Dim2560 scale = MapScale.Dim2560
) )
case object Map99 case object Map96
extends MapInfo( extends MapInfo(
value = "map99", value = "map96",
checksum = 846603446L, checksum = 846603446L,
scale = MapScale.Dim4096 scale = MapScale.Dim4096
) )
case object Map98
extends MapInfo(
value = "map98",
checksum = 2810790213L,
scale = MapScale.Dim4096
)
case object Map97 case object Map97
extends MapInfo( extends MapInfo(
value = "map97", value = "map97",
checksum = 2810790213L,
scale = MapScale.Dim4096
)
case object Map98
extends MapInfo(
value = "map98",
checksum = 3654267088L, checksum = 3654267088L,
scale = MapScale.Dim4096 scale = MapScale.Dim4096
) )
case object Map96 case object Map99
extends MapInfo( extends MapInfo(
value = "map96", value = "map99",
checksum = 4113726460L, checksum = 4113726460L,
scale = MapScale.Dim4096 scale = MapScale.Dim4096
) )

View file

@ -116,13 +116,13 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
if (recipient.session.player.silenced) { if (recipient.session.player.silenced) {
sender.actor ! MessageResponse( sender.actor ! MessageResponse(
session, session,
ChatMsg(UNK_71, true, "", "@silence_disabled_ack", None), ChatMsg(UNK_229, true, "", "@silence_disabled_ack", None),
channel channel
) )
} else { } else {
sender.actor ! MessageResponse( sender.actor ! MessageResponse(
session, session,
ChatMsg(UNK_71, true, "", "@silence_enabled_ack", None), ChatMsg(UNK_229, true, "", "@silence_enabled_ack", None),
channel channel
) )
} }
@ -130,14 +130,14 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
case None => case None =>
sender.actor ! MessageResponse( sender.actor ! MessageResponse(
session, session,
ChatMsg(UNK_71, true, "", s"unknown player '$name'", None), ChatMsg(UNK_229, true, "", s"unknown player '$name'", None),
channel channel
) )
} }
case (Some(sender), _, _, error) => case (Some(sender), _, _, error) =>
sender.actor ! MessageResponse( sender.actor ! MessageResponse(
session, session,
ChatMsg(UNK_71, false, "", error.getOrElse("usage: /silence <name> [<time>]"), None), ChatMsg(UNK_229, false, "", error.getOrElse("usage: /silence <name> [<time>]"), None),
channel channel
) )