mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-28 08:19:15 +00:00
commit
3bb878ac10
43 changed files with 1486 additions and 373 deletions
|
|
@ -3,11 +3,12 @@ package net.psforever.objects
|
|||
|
||||
import net.psforever.objects.definition._
|
||||
import net.psforever.objects.definition.converter.{CommandDetonaterConverter, LockerContainerConverter, REKConverter}
|
||||
import net.psforever.objects.serverobject.doors.DoorDefinition
|
||||
import net.psforever.objects.equipment.CItem.DeployedItem
|
||||
import net.psforever.objects.equipment._
|
||||
import net.psforever.objects.inventory.InventoryTile
|
||||
import net.psforever.objects.terminals.{CertTerminalDefinition, OrderTerminalDefinition}
|
||||
import net.psforever.packet.game.objectcreate.ObjectClass
|
||||
import net.psforever.objects.serverobject.locks.IFFLockDefinition
|
||||
import net.psforever.objects.serverobject.terminals.{CertTerminalDefinition, OrderTerminalDefinition}
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
object GlobalDefinitions {
|
||||
|
|
@ -1242,4 +1243,9 @@ object GlobalDefinitions {
|
|||
order_terminal = new OrderTerminalDefinition
|
||||
val
|
||||
cert_terminal = new CertTerminalDefinition
|
||||
|
||||
val
|
||||
lock_external = new IFFLockDefinition
|
||||
val
|
||||
door = new DoorDefinition
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,8 +64,6 @@ class Player(private val name : String,
|
|||
/** Last medkituse. */
|
||||
var lastMedkit : Long = 0
|
||||
var death_by : Int = 0
|
||||
var doors : Array[Int] = Array.ofDim(120)
|
||||
var doorsTime : Array[Long] = Array.ofDim(120)
|
||||
var lastSeenStreamMessage : Array[Long] = Array.fill[Long](65535)(0L)
|
||||
var lastShotSeq_time : Int = -1
|
||||
/** The player is shooting. */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject
|
||||
|
||||
import net.psforever.objects.Player
|
||||
|
||||
//temporary location for these messages
|
||||
object CommonMessages {
|
||||
final case class Hack(player : Player)
|
||||
final case class ClearHack()
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
|
||||
/**
|
||||
* An object layered on top of the standard game object class that maintains an internal `ActorRef`.
|
||||
* A measure of synchronization can be managed using this `Actor`.
|
||||
*/
|
||||
abstract class PlanetSideServerObject extends PlanetSideGameObject {
|
||||
private var actor = ActorRef.noSender
|
||||
|
||||
/**
|
||||
* Retrieve a reference to the internal `Actor`.
|
||||
* @return the internal `ActorRef`
|
||||
*/
|
||||
def Actor : ActorRef = actor
|
||||
|
||||
/**
|
||||
* Assign an `Actor` to act for this server object.
|
||||
* This reference is only set once, that is, as long as the internal `ActorRef` directs to `Actor.noSender` (`null`).
|
||||
* @param control the `Actor` whose functionality will govern this server object
|
||||
* @return the current internal `ActorRef`
|
||||
*/
|
||||
def Actor_=(control : ActorRef) : ActorRef = {
|
||||
if(actor == ActorRef.noSender) {
|
||||
actor = control
|
||||
}
|
||||
actor
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.serverobject.doors.{Door, DoorControl, DoorDefinition}
|
||||
|
||||
/**
|
||||
* Wrapper `Class` designed to instantiate a `Door` server object.
|
||||
* @param ddef a `DoorDefinition` object, indicating the specific functionality of the resulting `Door`
|
||||
* @param id the globally unique identifier to which this `Door` will be registered
|
||||
*/
|
||||
class DoorObjectBuilder(private val ddef : DoorDefinition, private val id : Int) extends ServerObjectBuilder[Door] {
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
def Build(implicit context : ActorContext, guid : NumberPoolHub) : Door = {
|
||||
val obj = Door(ddef)
|
||||
guid.register(obj, id) //non-Actor GUID registration
|
||||
obj.Actor = context.actorOf(Props(classOf[DoorControl], obj), s"${ddef.Name}_${obj.GUID.guid}")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
object DoorObjectBuilder {
|
||||
/**
|
||||
* Overloaded constructor for a `DoorObjectBuilder`.
|
||||
* @param ddef a `DoorDefinition` object
|
||||
* @param id a globally unique identifier
|
||||
* @return a `DoorObjectBuilder` object
|
||||
*/
|
||||
def apply(ddef : DoorDefinition, id : Int) : DoorObjectBuilder = {
|
||||
new DoorObjectBuilder(ddef, id)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.serverobject.locks.{IFFLock, IFFLockControl, IFFLockDefinition}
|
||||
|
||||
/**
|
||||
* Wrapper `Class` designed to instantiate a door lock server object that is sensitive to user faction affiliation.
|
||||
* @param idef a `IFFLockDefinition` object, indicating the specific functionality
|
||||
* @param id the globally unique identifier to which this `IFFLock` will be registered
|
||||
*/
|
||||
class IFFLockObjectBuilder(private val idef : IFFLockDefinition, private val id : Int) extends ServerObjectBuilder[IFFLock] {
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
def Build(implicit context : ActorContext, guid : NumberPoolHub) : IFFLock = {
|
||||
val obj = IFFLock(idef)
|
||||
guid.register(obj, id) //non-Actor GUID registration
|
||||
obj.Actor = context.actorOf(Props(classOf[IFFLockControl], obj), s"${idef.Name}_${obj.GUID.guid}")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
object IFFLockObjectBuilder {
|
||||
/**
|
||||
* Overloaded constructor for a `IFFLockObjectBuilder`.
|
||||
* @param idef an `IFFLock` object
|
||||
* @param id a globally unique identifier
|
||||
* @return an `IFFLockObjectBuilder` object
|
||||
*/
|
||||
def apply(idef : IFFLockDefinition, id : Int) : IFFLockObjectBuilder = {
|
||||
new IFFLockObjectBuilder(idef, id)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.PlanetSideGameObject
|
||||
|
|
@ -7,9 +7,11 @@ import net.psforever.objects.guid.NumberPoolHub
|
|||
|
||||
/**
|
||||
* Wrapper `Trait` designed to be extended to implement custom object instantiation logic at the `ZoneMap` level.
|
||||
* @tparam A any object that extends from PlanetSideGameObject
|
||||
* @see `Zone.Init`
|
||||
*/
|
||||
trait ServerObjectBuilder {
|
||||
//TODO can we changed PlanetSideGameObject -> PlanetSideServerObject?
|
||||
trait ServerObjectBuilder[A <: PlanetSideGameObject] {
|
||||
/**
|
||||
* Instantiate and configure the given server object
|
||||
* (at a later time compared to the construction of the builder class).<br>
|
||||
|
|
@ -23,5 +25,5 @@ trait ServerObjectBuilder {
|
|||
* defaults to `null`
|
||||
* @return the object that was created and integrated into the `Zone`
|
||||
*/
|
||||
def Build(implicit context : ActorContext = null, guid : NumberPoolHub = null) : PlanetSideGameObject
|
||||
def Build(implicit context : ActorContext = null, guid : NumberPoolHub = null) : A
|
||||
}
|
||||
|
|
@ -1,21 +1,22 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
package net.psforever.objects.serverobject.builders
|
||||
|
||||
import net.psforever.objects.terminals.{Terminal, TerminalDefinition}
|
||||
import akka.actor.Props
|
||||
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl, TerminalDefinition}
|
||||
|
||||
/**
|
||||
* Wrapper `Class` designed to instantiate a `Terminal` server object.
|
||||
* @param tdef a `TerminalDefinition` object, indicating the specific functionality of the resulting `Terminal`
|
||||
* @param id the globally unique identifier to which this `Terminal` will be registered
|
||||
*/
|
||||
class TerminalObjectBuilder(private val tdef : TerminalDefinition, private val id : Int) extends ServerObjectBuilder {
|
||||
class TerminalObjectBuilder(private val tdef : TerminalDefinition, private val id : Int) extends ServerObjectBuilder[Terminal] {
|
||||
import akka.actor.ActorContext
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
||||
def Build(implicit context : ActorContext, guid : NumberPoolHub) : Terminal = {
|
||||
val obj = Terminal(tdef)
|
||||
guid.register(obj, id) //non-Actor GUID registration
|
||||
obj.Actor //it's necessary to register beforehand because the Actor name utilizes the GUID
|
||||
obj.Actor = context.actorOf(Props(classOf[TerminalControl], obj), s"${tdef.Name}_${obj.GUID.guid}")
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.doors
|
||||
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
/**
|
||||
* A temporary class to represent "facilities" and "structures."
|
||||
* @param id the map id of the base
|
||||
*/
|
||||
class Base(private val id : Int) {
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
|
||||
def Id : Int = id
|
||||
|
||||
def Faction : PlanetSideEmpire.Value = faction
|
||||
|
||||
def Faction_=(emp : PlanetSideEmpire.Value) : PlanetSideEmpire.Value = {
|
||||
faction = emp
|
||||
Faction
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.doors
|
||||
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.UseItemMessage
|
||||
|
||||
/**
|
||||
* A structure-owned server object that is a "door" that can open and can close.
|
||||
* @param ddef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class Door(private val ddef : DoorDefinition) extends PlanetSideServerObject {
|
||||
private var openState : Boolean = false
|
||||
private var lockState : Boolean = false
|
||||
|
||||
def Open : Boolean = openState
|
||||
|
||||
def Open_=(open : Boolean) : Boolean = {
|
||||
openState = open
|
||||
Open
|
||||
}
|
||||
|
||||
def Locked : Boolean = lockState
|
||||
|
||||
def Locked_=(lock : Boolean) : Boolean = {
|
||||
lockState = lock
|
||||
Locked
|
||||
}
|
||||
|
||||
def Use(player : Player, msg : UseItemMessage) : Door.Exchange = {
|
||||
if(!lockState && !openState) {
|
||||
openState = true
|
||||
Door.OpenEvent()
|
||||
}
|
||||
else if(openState) {
|
||||
openState = false
|
||||
Door.CloseEvent()
|
||||
}
|
||||
else {
|
||||
Door.NoEvent()
|
||||
}
|
||||
}
|
||||
|
||||
def Definition : DoorDefinition = ddef
|
||||
}
|
||||
|
||||
object Door {
|
||||
/**
|
||||
* Entry message into this `Door` that carries the request.
|
||||
* @param player the player who sent this request message
|
||||
* @param msg the original packet carrying the request
|
||||
*/
|
||||
final case class Use(player : Player, msg : UseItemMessage)
|
||||
|
||||
/**
|
||||
* A basic `Trait` connecting all of the actionable `Door` response messages.
|
||||
*/
|
||||
sealed trait Exchange
|
||||
|
||||
/**
|
||||
* Message that carries the result of the processed request message back to the original user (`player`).
|
||||
* @param player the player who sent this request message
|
||||
* @param msg the original packet carrying the request
|
||||
* @param response the result of the processed request
|
||||
*/
|
||||
final case class DoorMessage(player : Player, msg : UseItemMessage, response : Exchange)
|
||||
|
||||
/**
|
||||
* This door will open.
|
||||
*/
|
||||
final case class OpenEvent() extends Exchange
|
||||
|
||||
/**
|
||||
* This door will close.
|
||||
*/
|
||||
final case class CloseEvent() extends Exchange
|
||||
|
||||
/**
|
||||
* This door will do nothing.
|
||||
*/
|
||||
final case class NoEvent() extends Exchange
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
def apply(tdef : DoorDefinition) : Door = {
|
||||
new Door(tdef)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.doors
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Door`.
|
||||
* @param door the `Door` object being governed
|
||||
*/
|
||||
class DoorControl(door : Door) extends Actor {
|
||||
def receive : Receive = {
|
||||
case Door.Use(player, msg) =>
|
||||
sender ! Door.DoorMessage(player, msg, door.Use(player, msg))
|
||||
|
||||
case _ =>
|
||||
sender ! Door.NoEvent()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.doors
|
||||
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
/**
|
||||
* The definition for any `Door`.
|
||||
* Object Id 242 is a generic door.
|
||||
*/
|
||||
class DoorDefinition extends ObjectDefinition(242) {
|
||||
Name = "door"
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.locks
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
import net.psforever.types.Vector3
|
||||
|
||||
/**
|
||||
* A structure-owned server object that is a "door lock."<br>
|
||||
* <br>
|
||||
* The "door lock" exerts an "identify friend or foe" field that detects the faction affiliation of a target player.
|
||||
* It also indirectly inherits faction affiliation from the structure to which it is connected
|
||||
* or it can be "hacked" whereupon the person exploiting it leaves their "faction" as the aforementioned affiliated faction.
|
||||
* The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper.
|
||||
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class IFFLock(private val idef : IFFLockDefinition) extends PlanetSideServerObject {
|
||||
/**
|
||||
* An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
|
||||
*/
|
||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
||||
|
||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
||||
|
||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
||||
|
||||
/**
|
||||
* Set the hack state of this object by recording important information about the player that caused it.
|
||||
* Set the hack state if there is no current hack state.
|
||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
||||
* @param agent a `Player`, or no player
|
||||
* @return the player hack entry
|
||||
*/
|
||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
||||
hackedBy match {
|
||||
case None =>
|
||||
//set the hack state if there is no current hack state
|
||||
if(agent.isDefined) {
|
||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||
}
|
||||
case Some(_) =>
|
||||
//clear the hack state
|
||||
if(agent.isEmpty) {
|
||||
hackedBy = None
|
||||
}
|
||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||
}
|
||||
}
|
||||
HackedBy
|
||||
}
|
||||
|
||||
def Definition : IFFLockDefinition = idef
|
||||
}
|
||||
|
||||
object IFFLock {
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
def apply(idef : IFFLockDefinition) : IFFLock = {
|
||||
new IFFLock(idef)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.locks
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.serverobject.CommonMessages
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `IFFLock`.
|
||||
* @param lock the `IFFLock` object being governed
|
||||
* @see `CommonMessages`
|
||||
*/
|
||||
class IFFLockControl(lock : IFFLock) extends Actor {
|
||||
def receive : Receive = {
|
||||
case CommonMessages.Hack(player) =>
|
||||
lock.HackedBy = player
|
||||
|
||||
case CommonMessages.ClearHack() =>
|
||||
lock.HackedBy = None
|
||||
|
||||
case _ => ; //no default message
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.serverobject.locks
|
||||
|
||||
import net.psforever.objects.definition.ObjectDefinition
|
||||
|
||||
/**
|
||||
* The definition for any `IFFLock`.
|
||||
* Object Id 451 is a generic external lock.
|
||||
*/
|
||||
class IFFLockDefinition extends ObjectDefinition(451) {
|
||||
Name = "iff_lock"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects.InfantryLoadout.Simplification
|
||||
import net.psforever.objects.{Player, Tool}
|
||||
|
|
@ -1,55 +1,59 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, Props}
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.inventory.InventoryItem
|
||||
import net.psforever.packet.game.ItemTransactionMessage
|
||||
import net.psforever.types.{ExoSuitType, PlanetSideEmpire, TransactionType}
|
||||
import net.psforever.objects.serverobject.PlanetSideServerObject
|
||||
import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID}
|
||||
import net.psforever.types.{ExoSuitType, TransactionType, Vector3}
|
||||
|
||||
/**
|
||||
* na
|
||||
* A structure-owned server object that is a "terminal" that can be accessed for amenities and services.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
class Terminal(tdef : TerminalDefinition) extends PlanetSideGameObject {
|
||||
/** Internal reference to the `Actor` for this `Terminal`, sets up by this `Terminal`. */
|
||||
private var actor = ActorRef.noSender
|
||||
class Terminal(tdef : TerminalDefinition) extends PlanetSideServerObject {
|
||||
/**
|
||||
* An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received.
|
||||
*/
|
||||
private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None
|
||||
|
||||
def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy
|
||||
|
||||
def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent))
|
||||
|
||||
/**
|
||||
* Get access to the internal `TerminalControl` `Actor` for this `Terminal`.
|
||||
* If called for the first time, create the said `Actor`.
|
||||
* Must be called only after the globally unique identifier has been set.
|
||||
* @param context the `ActorContext` under which this `Terminal`'s `Actor` will be created
|
||||
* @return the `Terminal`'s `Actor`
|
||||
* Set the hack state of this object by recording important information about the player that caused it.
|
||||
* Set the hack state if there is no current hack state.
|
||||
* Override the hack state with a new hack state if the new user has different faction affiliation.
|
||||
* @param agent a `Player`, or no player
|
||||
* @return the player hack entry
|
||||
*/
|
||||
def Actor(implicit context : ActorContext) : ActorRef = {
|
||||
if(actor == ActorRef.noSender) {
|
||||
actor = context.actorOf(Props(classOf[TerminalControl], this), s"${tdef.Name}_${GUID.guid}")
|
||||
def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = {
|
||||
hackedBy match {
|
||||
case None =>
|
||||
//set the hack state if there is no current hack state
|
||||
if(agent.isDefined) {
|
||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||
}
|
||||
case Some(_) =>
|
||||
//clear the hack state
|
||||
if(agent.isEmpty) {
|
||||
hackedBy = None
|
||||
}
|
||||
//override the hack state with a new hack state if the new user has different faction affiliation
|
||||
else if(agent.get.Faction != hackedBy.get._1.Faction) {
|
||||
hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position)
|
||||
}
|
||||
}
|
||||
actor
|
||||
HackedBy
|
||||
}
|
||||
|
||||
//the following fields and related methods are neither finalized no integrated; GOTO Request
|
||||
private var faction : PlanetSideEmpire.Value = PlanetSideEmpire.NEUTRAL
|
||||
private var hackedBy : Option[PlanetSideEmpire.Value] = None
|
||||
//the following fields and related methods are neither finalized nor integrated; GOTO Request
|
||||
private var health : Int = 100 //TODO not real health value
|
||||
|
||||
def Faction : PlanetSideEmpire.Value = faction
|
||||
|
||||
def HackedBy : Option[PlanetSideEmpire.Value] = hackedBy
|
||||
|
||||
def Health : Int = health
|
||||
|
||||
def Convert(toFaction : PlanetSideEmpire.Value) : Unit = {
|
||||
hackedBy = None
|
||||
faction = toFaction
|
||||
}
|
||||
|
||||
def HackedBy(toFaction : Option[PlanetSideEmpire.Value]) : Unit = {
|
||||
hackedBy = if(toFaction.contains(faction)) { None } else { toFaction }
|
||||
}
|
||||
|
||||
def Damaged(dam : Int) : Unit = {
|
||||
health = Math.max(0, Health - dam)
|
||||
}
|
||||
|
|
@ -148,14 +152,11 @@ object Terminal {
|
|||
*/
|
||||
final case class InfantryLoadout(exosuit : ExoSuitType.Value, subtype : Int = 0, holsters : List[InventoryItem], inventory : List[InventoryItem]) extends Exchange
|
||||
|
||||
/**
|
||||
* Overloaded constructor.
|
||||
* @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields
|
||||
*/
|
||||
def apply(tdef : TerminalDefinition) : Terminal = {
|
||||
new Terminal(tdef)
|
||||
}
|
||||
|
||||
import net.psforever.packet.game.PlanetSideGUID
|
||||
def apply(guid : PlanetSideGUID, tdef : TerminalDefinition) : Terminal = {
|
||||
val obj = new Terminal(tdef)
|
||||
obj.GUID = guid
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import akka.actor.Actor
|
||||
|
||||
/**
|
||||
* An `Actor` that handles messages being dispatched to a specific `Terminal`.<br>
|
||||
* <br>
|
||||
* For now, the only important message being managed is `Terminal.Request`.
|
||||
* An `Actor` that handles messages being dispatched to a specific `Terminal`.
|
||||
* @param term the `Terminal` object being governed
|
||||
*/
|
||||
class TerminalControl(term : Terminal) extends Actor {
|
||||
|
|
@ -14,18 +12,6 @@ class TerminalControl(term : Terminal) extends Actor {
|
|||
case Terminal.Request(player, msg) =>
|
||||
sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg))
|
||||
|
||||
case TemporaryTerminalMessages.Convert(fact) =>
|
||||
term.Convert(fact)
|
||||
|
||||
case TemporaryTerminalMessages.Hacked(fact) =>
|
||||
term.HackedBy(fact)
|
||||
|
||||
case TemporaryTerminalMessages.Damaged(dam) =>
|
||||
term.Damaged(dam)
|
||||
|
||||
case TemporaryTerminalMessages.Repaired(rep) =>
|
||||
term.Repair(rep)
|
||||
|
||||
case _ =>
|
||||
sender ! Terminal.NoDeal()
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
package net.psforever.objects.serverobject.terminals
|
||||
|
||||
import net.psforever.objects._
|
||||
import net.psforever.objects.definition._
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.terminals
|
||||
|
||||
import net.psforever.types.PlanetSideEmpire
|
||||
|
||||
//temporary location for these temporary messages
|
||||
object TemporaryTerminalMessages {
|
||||
//TODO send original packets along with these messages
|
||||
final case class Convert(faction : PlanetSideEmpire.Value)
|
||||
final case class Hacked(faction : Option[PlanetSideEmpire.Value])
|
||||
final case class Damaged(dm : Int)
|
||||
final case class Repaired(rep : Int)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.{ActorContext, ActorRef, Props}
|
||||
import net.psforever.objects.serverobject.doors.Base
|
||||
import net.psforever.objects.{PlanetSideGameObject, Player}
|
||||
import net.psforever.objects.equipment.Equipment
|
||||
import net.psforever.objects.guid.NumberPoolHub
|
||||
|
|
@ -48,6 +49,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
/** Used by the `Zone` to coordinate `Equipment` dropping and collection requests. */
|
||||
private var ground : ActorRef = ActorRef.noSender
|
||||
|
||||
private var bases : List[Base] = List()
|
||||
|
||||
/**
|
||||
* Establish the basic accessible conditions necessary for a functional `Zone`.<br>
|
||||
* <br>
|
||||
|
|
@ -69,6 +72,8 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
Map.LocalObjects.foreach({ builderObject =>
|
||||
builderObject.Build
|
||||
})
|
||||
|
||||
MakeBases(Map.LocalBases)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +174,15 @@ class Zone(private val zoneId : String, zoneMap : ZoneMap, zoneNumber : Int) {
|
|||
*/
|
||||
def Ground : ActorRef = ground
|
||||
|
||||
def MakeBases(num : Int) : List[Base] = {
|
||||
bases = (0 to num).map(id => new Base(id)).toList
|
||||
bases
|
||||
}
|
||||
|
||||
def Base(id : Int) : Option[Base] = {
|
||||
bases.lift(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide bulk correspondence on all map entities that can be composed into packet messages and reported to a client.
|
||||
* These messages are sent in this fashion at the time of joining the server:<br>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.zones
|
||||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.serverobject.locks.IFFLock
|
||||
|
||||
/**
|
||||
* na
|
||||
|
|
@ -13,8 +14,48 @@ class ZoneActor(zone : Zone) extends Actor {
|
|||
def receive : Receive = {
|
||||
case Zone.Init() =>
|
||||
zone.Init
|
||||
ZoneSetupCheck()
|
||||
|
||||
case msg =>
|
||||
log.warn(s"Received unexpected message - $msg")
|
||||
}
|
||||
|
||||
def ZoneSetupCheck(): Unit = {
|
||||
def guid(id : Int) = zone.GUID(id)
|
||||
val map = zone.Map
|
||||
val slog = org.log4s.getLogger(s"zone/${zone.Id}/sanity")
|
||||
|
||||
//check base to object associations
|
||||
map.ObjectToBase.foreach({ case((object_guid, base_id)) =>
|
||||
if(zone.Base(base_id).isEmpty) {
|
||||
slog.error(s"expected a base #$base_id")
|
||||
}
|
||||
if(guid(object_guid).isEmpty) {
|
||||
slog.error(s"expected object id $object_guid to exist, but it did not")
|
||||
}
|
||||
})
|
||||
|
||||
//check door to locks association
|
||||
import net.psforever.objects.serverobject.doors.Door
|
||||
map.DoorToLock.foreach({ case((door_guid, lock_guid)) =>
|
||||
try {
|
||||
if(!guid(door_guid).get.isInstanceOf[Door]) {
|
||||
slog.error(s"expected id $door_guid to be a door, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected a door, but looking for uninitialized object $door_guid")
|
||||
}
|
||||
try {
|
||||
if(!guid(lock_guid).get.isInstanceOf[IFFLock]) {
|
||||
slog.error(s"expected id $lock_guid to be an IFF locks, but it was not")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case _ : Exception =>
|
||||
slog.error(s"expected an IFF locks, but looking for uninitialized object $lock_guid")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.objects.zones
|
||||
|
||||
import net.psforever.objects.serverobject.builders.ServerObjectBuilder
|
||||
|
||||
/**
|
||||
* The fixed instantiation and relation of a series of server objects.<br>
|
||||
* <br>
|
||||
|
|
@ -12,32 +14,55 @@ package net.psforever.objects.zones
|
|||
* Use it as a blueprint.<br>
|
||||
* <br>
|
||||
* The "training zones" are the best example of the difference between a `ZoneMap` and a `Zone.`
|
||||
* ("Course" will be used as an unofficial location and layout descriptor.)
|
||||
* `tzdrtr` is the Terran Republic driving course.
|
||||
* `tzdrvs` is the Vanu Sovereignty driving course.
|
||||
* While each course can have different objects and object states (`Zone`),
|
||||
* both courses have the same basic server objects because they are built from the same blueprint (`ZoneMap`).
|
||||
* While each course can have different objects and object states, i.e., a `Zone`,
|
||||
* both of these courses utilize the same basic server object layout because they are built from the same blueprint, i.e., a `ZoneMap`.
|
||||
* @param name the privileged name that can be used as the first parameter in the packet `LoadMapMessage`
|
||||
* @see `ServerObjectBuilder`<br>
|
||||
* `LoadMapMessage`
|
||||
*/
|
||||
class ZoneMap(private val name : String) {
|
||||
private var localObjects : List[ServerObjectBuilder] = List()
|
||||
private var localObjects : List[ServerObjectBuilder[_]] = List()
|
||||
private var linkDoorLock : Map[Int, Int] = Map()
|
||||
private var linkObjectBase : Map[Int, Int] = Map()
|
||||
private var numBases : Int = 0
|
||||
|
||||
def Name : String = name
|
||||
|
||||
/**
|
||||
* Append the builder for a server object to the list of builders known to this `ZoneMap`.
|
||||
* @param obj the builder for a server object
|
||||
*/
|
||||
def LocalObject(obj : ServerObjectBuilder) : Unit = {
|
||||
localObjects = localObjects :+ obj
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of all server object builder wrappers that have been assigned to this `ZoneMap`.
|
||||
* @return the `List` of all `ServerObjectBuilders` known to this `ZoneMap`
|
||||
*/
|
||||
def LocalObjects : List[ServerObjectBuilder] = {
|
||||
def LocalObjects : List[ServerObjectBuilder[_]] = {
|
||||
localObjects
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the builder for a server object to the list of builders known to this `ZoneMap`.
|
||||
* @param obj the builder for a server object
|
||||
*/
|
||||
def LocalObject(obj : ServerObjectBuilder[_]) : Unit = {
|
||||
localObjects = localObjects :+ obj
|
||||
}
|
||||
|
||||
def LocalBases : Int = numBases
|
||||
|
||||
def LocalBases_=(num : Int) : Int = {
|
||||
numBases = math.max(0, num)
|
||||
LocalBases
|
||||
}
|
||||
|
||||
def ObjectToBase : Map[Int, Int] = linkObjectBase
|
||||
|
||||
def ObjectToBase(object_guid : Int, base_id : Int) : Unit = {
|
||||
linkObjectBase = linkObjectBase ++ Map(object_guid -> base_id)
|
||||
}
|
||||
|
||||
def DoorToLock : Map[Int, Int] = linkDoorLock
|
||||
|
||||
def DoorToLock(door_guid : Int, lock_guid : Int) = {
|
||||
linkDoorLock = linkDoorLock ++ Map(door_guid -> lock_guid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,7 +359,7 @@ object GamePacketOpcode extends Enumeration {
|
|||
// OPCODES 0x20-2f
|
||||
case 0x20 => noDecoder(UnknownMessage32)
|
||||
case 0x21 => game.ActionProgressMessage.decode
|
||||
case 0x22 => noDecoder(ActionCancelMessage)
|
||||
case 0x22 => game.ActionCancelMessage.decode
|
||||
case 0x23 => noDecoder(ActionCancelAcknowledgeMessage)
|
||||
case 0x24 => game.SetEmpireMessage.decode
|
||||
case 0x25 => game.EmoteMsg.decode
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game
|
||||
|
||||
import net.psforever.packet.{GamePacketOpcode, Marshallable, PlanetSideGamePacket}
|
||||
import scodec.Codec
|
||||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param player_guid na
|
||||
* @param object_guid na
|
||||
* @param unk na
|
||||
*/
|
||||
final case class ActionCancelMessage(player_guid : PlanetSideGUID,
|
||||
object_guid : PlanetSideGUID,
|
||||
unk : Int)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = ActionCancelMessage
|
||||
def opcode = GamePacketOpcode.ActionCancelMessage
|
||||
def encode = ActionCancelMessage.encode(this)
|
||||
}
|
||||
|
||||
object ActionCancelMessage extends Marshallable[ActionCancelMessage] {
|
||||
implicit val codec : Codec[ActionCancelMessage] = (
|
||||
("player_guid" | PlanetSideGUID.codec) ::
|
||||
("object_guid" | PlanetSideGUID.codec) ::
|
||||
("unk" | uint4L)
|
||||
).as[ActionCancelMessage]
|
||||
}
|
||||
|
|
@ -6,21 +6,65 @@ import scodec.Codec
|
|||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
*
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param unk3 na
|
||||
* @param unk4 na
|
||||
* @param unk5 na
|
||||
* @param unk6 na
|
||||
* @param unk7 na
|
||||
*/
|
||||
* An `Enumeration` of the various states and activities of the hacking process.
|
||||
* These values are closely tied to the condition of the hacking progress bar and/or the condition of the hacked object.<br>
|
||||
* <br>
|
||||
* `Start` initially displays the hacking progress bar.<br>
|
||||
* `Ongoing` is a neutral state that keeps the progress bar displayed while its value updates. (unconfirmed?)<br>
|
||||
* `Finished` disposes of the hacking progress bar. It does not, by itself, mean the hack was successful.<br>
|
||||
* `Hacked` modifies the target of the hack.<br>
|
||||
* `HackCleared` modifies the target of the hack, opposite of `Hacked`.
|
||||
*/
|
||||
object HackState extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
Unknown0,
|
||||
Start,
|
||||
Unknown2,
|
||||
Ongoing,
|
||||
Finished,
|
||||
Unknown5,
|
||||
Hacked,
|
||||
HackCleared
|
||||
= Value
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint8L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatched by the server to control the process of hacking.<br>
|
||||
* <br>
|
||||
* Part of the hacking process is regulated by the server while another part of it is automatically reset by the client.
|
||||
* The visibility, update, and closing of the hacking progress bar must be handled manually, for each tick.
|
||||
* When hacking is complete, using the appropriate `HackState` will cue the target to be affected by the hack.
|
||||
* Terminals and door IFF panels will temporarily expose their functionality;
|
||||
* the faction association of vehicles will be converted permanently;
|
||||
* a protracted process of a base conversion will be enacted; etc..
|
||||
* This transfer of faction association occurs to align the target with the faction of the hacking player (as indicated).
|
||||
* The client will select the faction without needing to be explicitly told
|
||||
* and will select the appropriate action to enact upon the target.
|
||||
* Upon the hack's completion, the target on the client will automatically revert back to its original state, if possible.
|
||||
* (It will still be necessary to alert this change from the server's perspective.)
|
||||
* @param unk1 na;
|
||||
* hack type?
|
||||
* @param target_guid the target of the hack
|
||||
* @param player_guid the player
|
||||
* @param progress the amount of progress visible;
|
||||
* visible range is 0 - 100
|
||||
* @param unk5 na;
|
||||
* often a large number;
|
||||
* doesn't seem to be `char_id`?
|
||||
* @param hack_state hack state
|
||||
* @param unk7 na;
|
||||
* usually, 8?
|
||||
*/
|
||||
final case class HackMessage(unk1 : Int,
|
||||
unk2 : Int,
|
||||
unk3 : Int,
|
||||
unk4 : Int,
|
||||
target_guid : PlanetSideGUID,
|
||||
player_guid : PlanetSideGUID,
|
||||
progress : Int,
|
||||
unk5 : Long,
|
||||
unk6 : Int,
|
||||
hack_state : HackState.Value,
|
||||
unk7 : Long)
|
||||
extends PlanetSideGamePacket {
|
||||
type Packet = HackMessage
|
||||
|
|
@ -30,12 +74,12 @@ final case class HackMessage(unk1 : Int,
|
|||
|
||||
object HackMessage extends Marshallable[HackMessage] {
|
||||
implicit val codec : Codec[HackMessage] = (
|
||||
("unk1" | uint2L) ::
|
||||
("unk2" | uint16L) ::
|
||||
("unk3" | uint16L) ::
|
||||
("unk4" | uint8L) ::
|
||||
("unk5" | uint32L) ::
|
||||
("unk6" | uint8L) ::
|
||||
("unk7" | uint32L)
|
||||
).as[HackMessage]
|
||||
("unk1" | uint2L) ::
|
||||
("object_guid" | PlanetSideGUID.codec) ::
|
||||
("player_guid" | PlanetSideGUID.codec) ::
|
||||
("progress" | uint8L) ::
|
||||
("unk5" | uint32L) ::
|
||||
("hack_state" | HackState.codec) ::
|
||||
("unk7" | uint32L)
|
||||
).as[HackMessage]
|
||||
}
|
||||
|
|
|
|||
29
common/src/test/scala/game/ActionCancelMessageTest.scala
Normal file
29
common/src/test/scala/game/ActionCancelMessageTest.scala
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game
|
||||
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game._
|
||||
import scodec.bits._
|
||||
|
||||
class ActionCancelMessageTest extends Specification {
|
||||
val string = hex"22 201ee01a10"
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case ActionCancelMessage(player_guid, object_guid, unk) =>
|
||||
player_guid mustEqual PlanetSideGUID(7712)
|
||||
object_guid mustEqual PlanetSideGUID(6880)
|
||||
unk mustEqual 1
|
||||
case _ =>
|
||||
ko
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = ActionCancelMessage(PlanetSideGUID(7712), PlanetSideGUID(6880), 1)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,13 @@ class HackMessageTest extends Specification {
|
|||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case HackMessage(unk1, unk2, unk3, unk4, unk5, unk6, unk7) =>
|
||||
case HackMessage(unk1, target_guid, player_guid, progress, unk5, hack_state, unk7) =>
|
||||
unk1 mustEqual 0
|
||||
unk2 mustEqual 1024
|
||||
unk3 mustEqual 3607
|
||||
unk4 mustEqual 0
|
||||
target_guid mustEqual PlanetSideGUID(1024)
|
||||
player_guid mustEqual PlanetSideGUID(3607)
|
||||
progress mustEqual 0
|
||||
unk5 mustEqual 3212836864L
|
||||
unk6 mustEqual 1
|
||||
hack_state mustEqual HackState.Start
|
||||
unk7 mustEqual 8L
|
||||
case _ =>
|
||||
ko
|
||||
|
|
@ -26,7 +26,7 @@ class HackMessageTest extends Specification {
|
|||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = HackMessage(0,1024,3607,0,3212836864L,1,8L)
|
||||
val msg = HackMessage(0, PlanetSideGUID(1024), PlanetSideGUID(3607), 0, 3212836864L, HackState.Start, 8L)
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue