unified code paths for dropping an item, and reinforced code path for picking an item back up; LocalService will handle some of the work now; ActionResultMessage embraces its simplistic nature (we don't have enough error messages)

This commit is contained in:
FateJH 2018-05-22 19:13:59 -04:00
parent fdf05337fd
commit f6f7ad5617
12 changed files with 192 additions and 126 deletions

View file

@ -386,27 +386,15 @@ object Zone {
final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int]) final case class NoValidSpawnPoint(zone_number : Int, spawn_group : Option[Int])
} }
/** object Ground {
* Message to relinguish an item and place in on the ground. final case class DropItem(item : Equipment, pos : Vector3, orient : Vector3)
* @param item the piece of `Equipment` final case class ItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
* @param pos where it is dropped final case class CanNotDropItem(item : Equipment)
* @param orient in which direction it is facing when dropped
*/
final case class DropItemOnGround(item : Equipment, pos : Vector3, orient : Vector3)
/** final case class PickupItem(item_guid : PlanetSideGUID)
* Message to attempt to acquire an item from the ground (before somoene else?). final case class ItemInHand(item : Equipment)
* @param player who wants the piece of `Equipment` final case class CanNotPickupItem(item_guid : PlanetSideGUID)
* @param item_guid the unique identifier of the piece of `Equipment` }
*/
final case class GetItemOnGround(player : Player, item_guid : PlanetSideGUID)
/**
* Message to give an item from the ground to a specific user.
* @param player who wants the piece of `Equipment`
* @param item the piece of `Equipment`
*/
final case class ItemFromGround(player : Player, item : Equipment)
object Vehicle { object Vehicle {
final case class Spawn(vehicle : Vehicle) final case class Spawn(vehicle : Vehicle)

View file

@ -50,10 +50,10 @@ class ZoneActor(zone : Zone) extends Actor {
zone.Population forward msg zone.Population forward msg
//frwd to Ground Actor //frwd to Ground Actor
case msg @ Zone.DropItemOnGround => case msg @ Zone.Ground.DropItem =>
zone.Ground forward msg zone.Ground forward msg
case msg @ Zone.GetItemOnGround => case msg @ Zone.Ground.PickupItem =>
zone.Ground forward msg zone.Ground forward msg
//frwd to Vehicle Actor //frwd to Vehicle Actor

View file

@ -16,18 +16,22 @@ class ZoneGroundActor(equipmentOnGround : ListBuffer[Equipment]) extends Actor {
//private[this] val log = org.log4s.getLogger //private[this] val log = org.log4s.getLogger
def receive : Receive = { def receive : Receive = {
case Zone.DropItemOnGround(item, pos, orient) => case Zone.Ground.DropItem(item, pos, orient) =>
item.Position = pos sender ! (FindItemOnGround(item.GUID) match {
item.Orientation = orient
equipmentOnGround += item
case Zone.GetItemOnGround(player, item_guid) =>
FindItemOnGround(item_guid) match {
case Some(item) =>
sender ! Zone.ItemFromGround(player, item)
case None => case None =>
org.log4s.getLogger.warn(s"item on ground $item_guid was requested by $player for pickup but was not found") equipmentOnGround += item
} Zone.Ground.ItemOnGround(item, pos, orient)
case Some(_) =>
Zone.Ground.CanNotDropItem(item)
})
case Zone.Ground.PickupItem(item_guid) =>
sender ! (FindItemOnGround(item_guid) match {
case Some(item) =>
Zone.Ground.ItemInHand(item)
case None =>
Zone.Ground.CanNotPickupItem(item_guid)
})
case _ => ; case _ => ;
} }

View file

@ -19,13 +19,18 @@ final case class ActionResultMessage(successful : Boolean,
} }
object ActionResultMessage extends Marshallable[ActionResultMessage] { object ActionResultMessage extends Marshallable[ActionResultMessage] {
def apply() : ActionResultMessage = { /**
ActionResultMessage(true, None) * A message where the result is always a pass.
} * @return an `ActionResultMessage` object
*/
def Pass : ActionResultMessage = ActionResultMessage(true, None)
def apply(error : Long) : ActionResultMessage = { /**
ActionResultMessage(false, Some(error)) * A message where the result is always a failure.
} * @param error the error code
* @return an `ActionResultMessage` object
*/
def Fail(error : Long) : ActionResultMessage = ActionResultMessage(false, Some(error))
implicit val codec : Codec[ActionResultMessage] = ( implicit val codec : Codec[ActionResultMessage] = (
("successful" | bool) >>:~ { res => ("successful" | bool) >>:~ { res =>

View file

@ -40,6 +40,14 @@ final case class ObjectDetachMessage(parent_guid : PlanetSideGUID,
} }
object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] { object ObjectDetachMessage extends Marshallable[ObjectDetachMessage] {
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient : Vector3) : ObjectDetachMessage = {
ObjectDetachMessage(parent_guid, child_guid, pos, orient.x, orient.y, orient.z)
}
def apply(parent_guid : PlanetSideGUID, child_guid : PlanetSideGUID, pos : Vector3, orient_z : Float) : ObjectDetachMessage = {
ObjectDetachMessage(parent_guid, child_guid, pos, 0, 0, orient_z)
}
implicit val codec : Codec[ObjectDetachMessage] = ( implicit val codec : Codec[ObjectDetachMessage] = (
("parent_guid" | PlanetSideGUID.codec) :: ("parent_guid" | PlanetSideGUID.codec) ::
("child_guid" | PlanetSideGUID.codec) :: ("child_guid" | PlanetSideGUID.codec) ::

View file

@ -38,7 +38,7 @@ class ActionResultMessageTest extends Specification {
} }
"encode (pass, minimal)" in { "encode (pass, minimal)" in {
val msg = ActionResultMessage() val msg = ActionResultMessage.Pass
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_pass pkt mustEqual string_pass
@ -52,7 +52,7 @@ class ActionResultMessageTest extends Specification {
} }
"encode (fail, minimal)" in { "encode (fail, minimal)" in {
val msg = ActionResultMessage(1) val msg = ActionResultMessage.Fail(1)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string_fail pkt mustEqual string_fail

View file

@ -26,10 +26,24 @@ class ObjectDetachMessageTest extends Specification {
} }
} }
"encode" in { "encode (1)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f) val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 0f, 0f, 270f)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string pkt mustEqual string
} }
"encode (2)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), Vector3(0f, 0f, 270f))
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
"encode (3)" in {
val msg = ObjectDetachMessage(PlanetSideGUID(2916), PlanetSideGUID(2502), Vector3(3567.1406f, 2988.0078f, 71.84375f), 270f)
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
pkt mustEqual string
}
} }

View file

@ -477,7 +477,7 @@ class ZoneGroundTest extends ActorTest {
assert(zone.EquipmentOnGround.isEmpty) assert(zone.EquipmentOnGround.isEmpty)
assert(item.Position == Vector3.Zero) assert(item.Position == Vector3.Zero)
assert(item.Orientation == Vector3.Zero) assert(item.Orientation == Vector3.Zero)
zone.Ground ! Zone.DropItemOnGround(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f)) zone.Ground ! Zone.Ground.DropItem(item, Vector3(1.1f, 2.2f, 3.3f), Vector3(4.4f, 5.5f, 6.6f))
expectNoMsg(Duration.create(100, "ms")) expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item)) assert(zone.EquipmentOnGround == List(item))
@ -490,17 +490,16 @@ class ZoneGroundTest extends ActorTest {
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!" system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-good") ! "!"
receiveOne(Duration.create(200, "ms")) //consume receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero) zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms")) expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item)) assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(10)) zone.Ground ! Zone.Ground.PickupItem(PlanetSideGUID(10))
val reply = receiveOne(Duration.create(100, "ms")) val reply = receiveOne(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround.isEmpty) assert(zone.EquipmentOnGround.isEmpty)
assert(reply.isInstanceOf[Zone.ItemFromGround]) assert(reply.isInstanceOf[Zone.Ground.ItemInHand])
assert(reply.asInstanceOf[Zone.ItemFromGround].player == player) assert(reply.asInstanceOf[Zone.Ground.ItemInHand].item == item)
assert(reply.asInstanceOf[Zone.ItemFromGround].item == item)
} }
"get item from ground (failure)" in { "get item from ground (failure)" in {
@ -508,11 +507,11 @@ class ZoneGroundTest extends ActorTest {
val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5)) val player = Player(Avatar("Chord", PlanetSideEmpire.TR, CharacterGender.Male, 0, 5))
system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!" system.actorOf(Props(classOf[ZoneTest.ZoneInitActor], zone), "get-item-test-fail") ! "!"
receiveOne(Duration.create(200, "ms")) //consume receiveOne(Duration.create(200, "ms")) //consume
zone.Ground ! Zone.DropItemOnGround(item, Vector3.Zero, Vector3.Zero) zone.Ground ! Zone.Ground.DropItem(item, Vector3.Zero, Vector3.Zero)
expectNoMsg(Duration.create(100, "ms")) expectNoMsg(Duration.create(100, "ms"))
assert(zone.EquipmentOnGround == List(item)) assert(zone.EquipmentOnGround == List(item))
zone.Ground ! Zone.GetItemOnGround(player, PlanetSideGUID(11)) //wrong guid zone.Ground ! Zone.Ground.PickupItem(PlanetSideGUID(11)) //wrong guid
expectNoMsg(Duration.create(500, "ms")) expectNoMsg(Duration.create(500, "ms"))
} }
} }

View file

@ -255,7 +255,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) => case AvatarResponse.ChangeAmmo(weapon_guid, weapon_slot, previous_guid, ammo_id, ammo_guid, ammo_data) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3(0,0,0), 0f, 0f, 0f)) sendResponse(ObjectDetachMessage(weapon_guid, previous_guid, Vector3.Zero, 0))
sendResponse( sendResponse(
ObjectCreateMessage( ObjectCreateMessage(
ammo_id, ammo_id,
@ -405,7 +405,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case LocalServiceResponse(_, guid, reply) => case LocalServiceResponse(_, guid, reply) =>
val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(0) } val tplayer_guid = if(player.HasGUID) { player.GUID} else { PlanetSideGUID(-1) }
reply match { reply match {
case LocalResponse.DoorOpens(door_guid) => case LocalResponse.DoorOpens(door_guid) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
@ -415,16 +415,21 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalResponse.DoorCloses(door_guid) => //door closes for everyone case LocalResponse.DoorCloses(door_guid) => //door closes for everyone
sendResponse(GenericObjectStateMsg(door_guid, 17)) sendResponse(GenericObjectStateMsg(door_guid, 17))
case LocalResponse.DropItem(id, item_guid, data) =>
if(tplayer_guid != guid) {
sendResponse(ObjectCreateMessage(id, item_guid, data))
}
case LocalResponse.HackClear(target_guid, unk1, unk2) => case LocalResponse.HackClear(target_guid, unk1, unk2) =>
sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)) sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2))
case LocalResponse.HackObject(target_guid, unk1, unk2) => case LocalResponse.HackObject(target_guid, unk1, unk2) =>
if(player.GUID != guid) { if(tplayer_guid != guid) {
sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2)) sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2))
} }
case LocalResponse.ProximityTerminalEffect(object_guid, effectState) => case LocalResponse.ProximityTerminalEffect(object_guid, effectState) =>
if(player.GUID != guid) { if(tplayer_guid != guid) {
sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState)) sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState))
} }
@ -465,7 +470,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
} }
case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) => case VehicleResponse.DetachFromRails(vehicle_guid, pad_guid, pad_position, pad_orientation_z) =>
sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), 0, 0, pad_orientation_z)) sendResponse(ObjectDetachMessage(pad_guid, vehicle_guid, pad_position + Vector3(0,0,0.5f), pad_orientation_z))
case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) => case VehicleResponse.InventoryState(obj, parent_guid, start, con_data) =>
if(tplayer_guid != guid) { if(tplayer_guid != guid) {
@ -844,12 +849,10 @@ class WorldSessionActor extends Actor with MDCContextAware {
}) })
//drop items on ground //drop items on ground
val pos = tplayer.Position val pos = tplayer.Position
val orient = tplayer.Orientation val orient = Vector3(0,0, tplayer.Orientation.z)
((dropHolsters ++ dropInventory).map(_.obj) ++ drop).foreach(obj => { ((dropHolsters ++ dropInventory).map(_.obj) ++ drop).foreach(obj => {
continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z)) //TODO make a sound when dropping stuff
sendResponse(ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z)) continent.Ground ! Zone.Ground.DropItem(obj, pos, orient)
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
}) })
sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true)) sendResponse(ItemTransactionResultMessage (msg.terminal_guid, TransactionType.Buy, true))
} }
@ -938,12 +941,9 @@ class WorldSessionActor extends Actor with MDCContextAware {
}) })
//drop stuff on ground //drop stuff on ground
val pos = tplayer.Position val pos = tplayer.Position
val orient = tplayer.Orientation val orient = Vector3(0,0, tplayer.Orientation.z)
((dropHolsters ++ dropInventory).map(_.obj)).foreach(obj => { ((dropHolsters ++ dropInventory).map(_.obj)).foreach(obj => {
continent.Ground ! Zone.DropItemOnGround(obj, pos, Vector3(0f, 0f, orient.z)) continent.Ground ! Zone.Ground.DropItem(obj, pos, orient)
sendResponse(ObjectDetachMessage(tplayer.GUID, obj.GUID, pos, 0f, 0f, orient.z))
val objDef = obj.Definition
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentOnGround(tplayer.GUID, pos, orient, objDef.ObjectId, obj.GUID, objDef.Packet.ConstructorData(obj).get))
}) })
case Terminal.VehicleLoadout(definition, weapons, inventory) => case Terminal.VehicleLoadout(definition, weapons, inventory) =>
@ -1177,7 +1177,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad) => case VehicleSpawnPad.ServerVehicleOverrideStart(vehicle, pad) =>
val vdef = vehicle.Definition val vdef = vehicle.Definition
if(vehicle.Seats(0).isOccupied) { if(vehicle.Seats(0).isOccupied) {
sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), 0, 0, pad.Orientation.z)) sendResponse(ObjectDetachMessage(pad.GUID, vehicle.GUID, pad.Position + Vector3(0, 0, 0.5f), pad.Orientation.z))
} }
ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, GlobalDefinitions.isFlightVehicle(vdef):Int) ServerVehicleOverride(vehicle, vdef.AutoPilotSpeed1, GlobalDefinitions.isFlightVehicle(vdef):Int)
@ -1375,6 +1375,62 @@ class WorldSessionActor extends Actor with MDCContextAware {
RequestSanctuaryZoneSpawn(player, zone_number) RequestSanctuaryZoneSpawn(player, zone_number)
} }
case Zone.Ground.ItemOnGround(item, pos, orient) =>
item.Position = pos
item.Orientation = Vector3(0,0, orient.z) //only one kind of rotation is important
val exclusionId = player.Find(item) match {
case Some(slotNum) =>
player.Slot(slotNum).Equipment = None
sendResponse(ObjectDetachMessage(player.GUID, item.GUID, pos, orient.z))
sendResponse(ActionResultMessage.Pass)
player.GUID //we're dropping it; don't need to see it dropped again
case None =>
PlanetSideGUID(0) //object is being introduced into the world upon drop
}
localService ! LocalServiceMessage(continent.Id, LocalAction.DropItem(exclusionId, item))
case Zone.Ground.CanNotDropItem(item) =>
log.warn(s"DropItem: $player tried to drop a $item on the ground, but he missed")
player.Find(item) match {
case None => //item in limbo
taskResolver ! GUIDTask.UnregisterEquipment(item)(continent.GUID)
case Some(_) => ;
}
case Zone.Ground.ItemInHand(item) =>
player.Fit(item) match {
case Some(slotNum) =>
val item_guid = item.GUID
val player_guid = player.GUID
player.Slot(slotNum).Equipment = item
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
item_guid,
ObjectCreateMessageParent(player_guid, slotNum),
definition.Packet.DetailedConstructorData(item).get
)
)
avatarService ! AvatarServiceMessage(continent.Id, if(player.VisibleSlots.contains(slotNum)) {
AvatarAction.EquipmentInHand(player_guid, player_guid, slotNum, item)
}
else {
AvatarAction.ObjectDelete(player_guid, item_guid)
})
sendResponse(ActionResultMessage.Pass)
case None =>
continent.Ground ! Zone.Ground.DropItem(item, item.Position, item.Orientation) //restore previous state
}
case Zone.Ground.CanNotPickupItem(item_guid) =>
continent.GUID(item_guid) match {
case Some(item) =>
log.warn(s"DropItem: finding a $item on the ground was suggested, but $player can not reach it")
case None =>
log.warn(s"DropItem: finding an item ($item_guid) on the ground was suggested, but $player can not see it")
}
case InterstellarCluster.ClientInitializationComplete() => case InterstellarCluster.ClientInitializationComplete() =>
StopBundlingPackets() StopBundlingPackets()
LivePlayerList.Add(sessionId, avatar) LivePlayerList.Add(sessionId, avatar)
@ -1467,31 +1523,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
//TacticsMessage //TacticsMessage
StopBundlingPackets() StopBundlingPackets()
case Zone.ItemFromGround(tplayer, item) =>
val obj_guid = item.GUID
val player_guid = tplayer.GUID
tplayer.Fit(item) match {
case Some(slot) =>
tplayer.Slot(slot).Equipment = item
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.ObjectDelete(player_guid, obj_guid))
val definition = item.Definition
sendResponse(
ObjectCreateDetailedMessage(
definition.ObjectId,
obj_guid,
ObjectCreateMessageParent(player_guid, slot),
definition.Packet.DetailedConstructorData(item).get
)
)
sendResponse(ActionResultMessage())
if(tplayer.VisibleSlots.contains(slot)) {
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.EquipmentInHand(player_guid, player_guid, slot, item))
}
case None =>
continent.Ground ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
}
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) => case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
progressBarUpdate.cancel progressBarUpdate.cancel
if(progressBarValue.isDefined) { if(progressBarValue.isDefined) {
@ -1607,18 +1638,17 @@ class WorldSessionActor extends Actor with MDCContextAware {
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
clientKeepAlive.cancel clientKeepAlive.cancel
clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient()) clientKeepAlive = context.system.scheduler.schedule(0 seconds, 500 milliseconds, self, PokeClient())
log.warn(PacketCoding.DecodePacket(hex"d2327e7b8a972b95113881003710").toString)
case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) => case msg @ CharacterCreateRequestMessage(name, head, voice, gender, empire) =>
log.info("Handling " + msg) log.info("Handling " + msg)
sendResponse(ActionResultMessage(true, None)) sendResponse(ActionResultMessage.Pass)
self ! ListAccountCharacters self ! ListAccountCharacters
case msg @ CharacterRequestMessage(charId, action) => case msg @ CharacterRequestMessage(charId, action) =>
log.info("Handling " + msg) log.info("Handling " + msg)
action match { action match {
case CharacterRequestAction.Delete => case CharacterRequestAction.Delete =>
sendResponse(ActionResultMessage(false, Some(1))) sendResponse(ActionResultMessage.Fail(1))
case CharacterRequestAction.Select => case CharacterRequestAction.Select =>
//TODO check if can spawn on last continent/location from player? //TODO check if can spawn on last continent/location from player?
//TODO if yes, get continent guid accessors //TODO if yes, get continent guid accessors
@ -1828,7 +1858,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
import scala.concurrent.duration._ import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
context.system.scheduler.scheduleOnce(50 milliseconds, self, UnregisterCorpseOnVehicleDisembark(player)) context.system.scheduler.scheduleOnce(50 milliseconds, self, UnregisterCorpseOnVehicleDisembark(player))
//sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0, 0, 0)) //sendResponse(ObjectDetachMessage(vehicle_guid, player.GUID, Vector3.Zero, 0))
//sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0))) //sendResponse(PlayerStateShiftMessage(ShiftState(1, Vector3.Zero, 0)))
} }
@ -1989,8 +2019,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val tailReloadValue : Int = if(xs.isEmpty) { 0 } else { xs.map(_.obj.asInstanceOf[AmmoBox].Capacity).reduceLeft(_ + _) } val tailReloadValue : Int = if(xs.isEmpty) { 0 } else { xs.map(_.obj.asInstanceOf[AmmoBox].Capacity).reduceLeft(_ + _) }
val sumReloadValue : Int = originalBoxCapacity + tailReloadValue val sumReloadValue : Int = originalBoxCapacity + tailReloadValue
val previousBox = tool.AmmoSlot.Box //current magazine in tool val previousBox = tool.AmmoSlot.Box //current magazine in tool
sendResponse(ObjectDetachMessage(tool.GUID, previousBox.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f)) sendResponse(ObjectDetachMessage(tool.GUID, previousBox.GUID, Vector3.Zero, 0f))
sendResponse(ObjectDetachMessage(player.GUID, box.GUID, Vector3(0f, 0f, 0f), 0f, 0f, 0f)) sendResponse(ObjectDetachMessage(player.GUID, box.GUID, Vector3.Zero, 0f))
obj.Inventory -= x.start //remove replacement ammo from inventory obj.Inventory -= x.start //remove replacement ammo from inventory
val ammoSlotIndex = tool.FireMode.AmmoSlotIndex val ammoSlotIndex = tool.FireMode.AmmoSlotIndex
tool.AmmoSlots(ammoSlotIndex).Box = box //put replacement ammo in tool tool.AmmoSlots(ammoSlotIndex).Box = box //put replacement ammo in tool
@ -2158,27 +2188,37 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(EmoteMsg(avatar_guid, emote)) sendResponse(EmoteMsg(avatar_guid, emote))
case msg @ DropItemMessage(item_guid) => case msg @ DropItemMessage(item_guid) =>
log.info("DropItem: " + msg) log.info(s"DropItem: $msg")
player.FreeHand.Equipment match { continent.GUID(item_guid) match {
case Some(item) => case Some(item : Equipment) =>
if(item.GUID == item_guid) { player.FreeHand.Equipment match {
val orient : Vector3 = Vector3(0f, 0f, player.Orientation.z) case Some(_) =>
player.FreeHand.Equipment = None if(item.GUID == item_guid) {
continent.Ground ! Zone.DropItemOnGround(item, player.Position, orient) continent.Ground ! Zone.Ground.DropItem(item, player.Position, player.Orientation)
sendResponse(ObjectDetachMessage(player.GUID, item.GUID, player.Position, 0f, 0f, player.Orientation.z)) }
val objDef = item.Definition case None =>
avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.EquipmentOnGround(player.GUID, player.Position, orient, objDef.ObjectId, item.GUID, objDef.Packet.ConstructorData(item).get)) log.warn(s"DropItem: $player wanted to drop a $item, but it wasn't at hand")
}
else {
log.warn(s"item in hand was ${item.GUID} but trying to drop $item_guid; nothing will be dropped")
} }
case Some(obj) => //TODO LLU
log.warn(s"DropItem: $player wanted to drop a $obj, but that isn't possible")
case None => case None =>
log.error(s"$player wanted to drop an item, but it was not in hand") log.warn(s"DropItem: $player wanted to drop an item ($item_guid), but it was nowhere to be found")
} }
case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) => case msg @ PickupItemMessage(item_guid, player_guid, unk1, unk2) =>
log.info("PickupItem: " + msg) log.info(s"PickupItem: $msg")
continent.Ground ! Zone.GetItemOnGround(player, item_guid) continent.GUID(item_guid) match {
case Some(item : Equipment) =>
player.Fit(item) match {
case Some(_) =>
continent.Ground ! Zone.Ground.PickupItem(item_guid)
case None => //skip
sendResponse(ActionResultMessage.Fail(16)) //error code?
}
case _ =>
log.warn(s"PickupItem: $player requested an item that doesn't exist in this zone; assume client-side garbage data")
sendResponse(ObjectDeleteMessage(item_guid, 0))
}
case msg @ ReloadMessage(item_guid, ammo_clip, unk1) => case msg @ ReloadMessage(item_guid, ammo_clip, unk1) =>
log.info("Reload: " + msg) log.info("Reload: " + msg)
@ -3847,14 +3887,14 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(InventoryItem(item2, destIndex)) => //yes, swap case Some(InventoryItem(item2, destIndex)) => //yes, swap
//cleanly shuffle items around to avoid losing icons //cleanly shuffle items around to avoid losing icons
//the next ObjectDetachMessage is necessary to avoid icons being lost, but only as part of this swap //the next ObjectDetachMessage is necessary to avoid icons being lost, but only as part of this swap
sendResponse(ObjectDetachMessage(source_guid, item_guid, Vector3.Zero, 0f, 0f, 0f)) sendResponse(ObjectDetachMessage(source_guid, item_guid, Vector3.Zero, 0f))
val item2_guid = item2.GUID val item2_guid = item2.GUID
destination.Slot(destIndex).Equipment = None //remove the swap item from destination destination.Slot(destIndex).Equipment = None //remove the swap item from destination
(indexSlot.Equipment = item2) match { (indexSlot.Equipment = item2) match {
case Some(_) => //item and item2 swapped places successfully case Some(_) => //item and item2 swapped places successfully
log.info(s"MoveItem: $item2 swapped to $source @ $index") log.info(s"MoveItem: $item2 swapped to $source @ $index")
//remove item2 from destination //remove item2 from destination
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, Vector3.Zero, 0f, 0f, 0f)) sendResponse(ObjectDetachMessage(destination_guid, item2_guid, Vector3.Zero, 0f))
destination match { destination match {
case obj : Vehicle => case obj : Vehicle =>
vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid)) vehicleService ! VehicleServiceMessage(s"${obj.Actor}", VehicleAction.UnstowEquipment(player_guid, item2_guid))
@ -3897,8 +3937,8 @@ class WorldSessionActor extends Actor with MDCContextAware {
val pos = source.Position val pos = source.Position
val sourceOrientZ = source.Orientation.z val sourceOrientZ = source.Orientation.z
val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ) val orient : Vector3 = Vector3(0f, 0f, sourceOrientZ)
continent.Actor ! Zone.DropItemOnGround(item2, pos, orient) continent.Ground ! Zone.Ground.DropItem(item2, pos, orient)
sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, 0f, 0f, sourceOrientZ)) //ground sendResponse(ObjectDetachMessage(destination_guid, item2_guid, pos, sourceOrientZ)) //ground
val objDef = item2.Definition val objDef = item2.Definition
destination match { destination match {
case obj : Vehicle => case obj : Vehicle =>
@ -3956,15 +3996,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
* @param item the item * @param item the item
*/ */
def NormalItemDrop(obj : PlanetSideGameObject with Container, zone : Zone, service : ActorRef)(item : Equipment) : Unit = { def NormalItemDrop(obj : PlanetSideGameObject with Container, zone : Zone, service : ActorRef)(item : Equipment) : Unit = {
val itemGUID = item.GUID continent.Ground ! Zone.Ground.DropItem(item, obj.Position, Vector3(0f, 0f, obj.Orientation.z))
val ang = obj.Orientation.z
val pos = obj.Position
val orient = Vector3(0f, 0f, ang)
item.Position = pos
item.Orientation = orient
zone.Ground ! Zone.DropItemOnGround(item, pos, orient)
val itemDef = item.Definition
service ! AvatarServiceMessage(zone.Id, AvatarAction.EquipmentOnGround(Service.defaultPlayerGUID, pos, orient, itemDef.ObjectId, itemGUID, itemDef.Packet.ConstructorData(item).get))
} }
/** /**

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.local package services.local
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.PlanetSideServerObject import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.zones.Zone import net.psforever.objects.zones.Zone
@ -12,6 +13,7 @@ object LocalAction {
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
final case class DropItem(player_guid : PlanetSideGUID, item : Equipment) extends Action
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action final case class ProximityTerminalEffect(player_guid : PlanetSideGUID, object_guid : PlanetSideGUID, effectState : Boolean) extends Action

View file

@ -1,6 +1,7 @@
// Copyright (c) 2017 PSForever // Copyright (c) 2017 PSForever
package services.local package services.local
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound} import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound}
import net.psforever.types.Vector3 import net.psforever.types.Vector3
@ -9,6 +10,7 @@ object LocalResponse {
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
final case class DropItem(item_id : Int, item_guid : PlanetSideGUID, item_data : ConstructorData) extends Response
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response final case class ProximityTerminalEffect(object_guid : PlanetSideGUID, effectState : Boolean) extends Response

View file

@ -2,6 +2,7 @@
package services.local package services.local
import akka.actor.{Actor, Props} import akka.actor.{Actor, Props}
import net.psforever.packet.game.objectcreate.{DroppedItemData, PlacementData}
import services.local.support.{DoorCloseActor, HackClearActor} import services.local.support.{DoorCloseActor, HackClearActor}
import services.{GenericEventBus, Service} import services.{GenericEventBus, Service}
@ -46,6 +47,17 @@ class LocalService extends Actor {
LocalEvents.publish( LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid)) LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.DoorCloses(door_guid))
) )
case LocalAction.DropItem(player_guid, item) =>
val definition = item.Definition
val objectData = DroppedItemData(
PlacementData(item.Position, item.Orientation),
definition.Packet.ConstructorData(item).get
)
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid,
LocalResponse.DropItem(definition.ObjectId, item.GUID, objectData)
)
)
case LocalAction.HackClear(player_guid, target, unk1, unk2) => case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
LocalEvents.publish( LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2)) LocalServiceResponse(s"/$forChannel/Local", player_guid, LocalResponse.HackClear(target.GUID, unk1, unk2))