mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-03-14 01:20:34 +00:00
wrote comments for SquadState, SquadMembershipRequest, and SquadMembershipResponse; messaging for players joining and leaving squads in WSA and SquadService; char_id is now a required field for Avatars
This commit is contained in:
parent
60a11b8c52
commit
640a96ae9c
9 changed files with 237 additions and 118 deletions
|
|
@ -10,9 +10,8 @@ import net.psforever.types._
|
|||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
|
||||
class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : CharacterVoice.Value) {
|
||||
/** Character ID; a unique identifier corresponding to a database table row index */
|
||||
private var charId : Long = 0
|
||||
class Avatar(private val char_id : Long, val name : String, val faction : PlanetSideEmpire.Value, val sex : CharacterGender.Value, val head : Int, val voice : CharacterVoice.Value) {
|
||||
/** char_id, Character ID; a unique identifier corresponding to a database table row index */
|
||||
/** Battle Experience Points */
|
||||
private var bep : Long = 0
|
||||
/** Command Experience Points */
|
||||
|
|
@ -48,14 +47,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
|||
|
||||
private val deployables : DeployableToolbox = new DeployableToolbox
|
||||
|
||||
def CharId : Long = charId
|
||||
|
||||
def CharId_=(id : Long) : Long = {
|
||||
if(charId == 0) { //does not need to be set but can only set once
|
||||
charId = id
|
||||
}
|
||||
CharId
|
||||
}
|
||||
def CharId : Long = char_id
|
||||
|
||||
def BEP : Long = bep
|
||||
|
||||
|
|
@ -226,7 +218,7 @@ class Avatar(val name : String, val faction : PlanetSideEmpire.Value, val sex :
|
|||
|
||||
object Avatar {
|
||||
def apply(name : String, faction : PlanetSideEmpire.Value, sex : CharacterGender.Value, head : Int, voice : CharacterVoice.Value) : Avatar = {
|
||||
new Avatar(name, faction, sex, head, voice)
|
||||
new Avatar(0L, name, faction, sex, head, voice)
|
||||
}
|
||||
|
||||
def toString(avatar : Avatar) : String = s"${avatar.faction} ${avatar.name}"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,20 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
object MemberEvent extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val
|
||||
Add,
|
||||
Remove,
|
||||
Unknown2,
|
||||
UpdateZone,
|
||||
Unknown4
|
||||
= Value
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
||||
}
|
||||
|
||||
final case class SquadMemberEvent(unk1 : Int,
|
||||
unk2 : Int,
|
||||
char_id : Long,
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@ import scodec.Codec
|
|||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* SquadMembershipRequest is a Client->Server message for manipulating squad and platoon members.
|
||||
* @param request_type na
|
||||
* @param unk2 na
|
||||
* @param unk3 na
|
||||
* @param player_name na
|
||||
* @param unk5 na
|
||||
* Dispatched by the client as manipulation protocol for squad and platoon members.
|
||||
* Answerable by a `SquadMembershipResponse` packet.
|
||||
* @param request_type the purpose of the request
|
||||
* @param char_id a squad member unique identifier;
|
||||
* usually, the player being addresses by thie packet
|
||||
* @param unk3 na
|
||||
* @param player_name name of the player being affected, if applicable
|
||||
* @param unk5 na
|
||||
*/
|
||||
final case class SquadMembershipRequest(request_type : SquadRequestType.Value,
|
||||
unk2 : Long,
|
||||
char_id : Long,
|
||||
unk3 : Option[Long],
|
||||
player_name : String,
|
||||
unk5 : Option[Option[String]])
|
||||
|
|
|
|||
|
|
@ -7,31 +7,29 @@ import scodec.Codec
|
|||
import scodec.codecs._
|
||||
|
||||
/**
|
||||
* na
|
||||
* @param request_type the type of request being answered
|
||||
* Dispatched by the server as manipulation protocol for squad and platoon members.
|
||||
* Prompted by and answers for a `SquadMembershipRequest` packet.
|
||||
* @param request_type the purpose of the request
|
||||
* @param unk1 na
|
||||
* @param unk2 na
|
||||
* @param unk3 na
|
||||
* @param unk4 na
|
||||
* @param player_name the player being affected, if applicable
|
||||
* @param char_id a squad member unique identifier;
|
||||
* usually, the player being addresses by thie packet
|
||||
* @param other_id another squad member's unique identifier;
|
||||
* may be the same as `char_id`
|
||||
* @param player_name name of the player being affected, if applicable
|
||||
* @param unk5 na
|
||||
* @param unk6 na
|
||||
* @param unk6 na;
|
||||
* the internal field, the `Option[String]`, never seems to be set
|
||||
*/
|
||||
final case class SquadMembershipResponse(request_type : SquadRequestType.Value,
|
||||
unk1 : Int,
|
||||
unk2 : Int,
|
||||
unk3 : Long,
|
||||
unk4 : Option[Long],
|
||||
char_id : Long,
|
||||
other_id : Option[Long],
|
||||
player_name : String,
|
||||
unk5 : Boolean,
|
||||
unk6 : Option[Option[String]])
|
||||
extends PlanetSideGamePacket {
|
||||
/*
|
||||
if(response_type != 6 && response_type != 12)
|
||||
assert(unk5.isDefined, "unk5 field required")
|
||||
else
|
||||
assert(!unk5.isDefined, "unk5 defined but unk1 invalid value")
|
||||
*/
|
||||
type Packet = SquadMembershipResponse
|
||||
def opcode = GamePacketOpcode.SquadMembershipResponse
|
||||
def encode = SquadMembershipResponse.encode(this)
|
||||
|
|
@ -42,8 +40,8 @@ object SquadMembershipResponse extends Marshallable[SquadMembershipResponse] {
|
|||
"request_type" | SquadRequestType.codec >>:~ { d =>
|
||||
("unk1" | uint(5)) ::
|
||||
("unk2" | uint2) ::
|
||||
("unk3" | uint32L) ::
|
||||
("unk4" | conditional(d != SquadRequestType.Promote && d != SquadRequestType.PlatoonLeave, uint32L)) ::
|
||||
("char_id" | uint32L) ::
|
||||
("other_id" | conditional(d != SquadRequestType.Promote && d != SquadRequestType.PlatoonLeave, uint32L)) ::
|
||||
("player_name" | PacketHelpers.encodedWideStringAligned(5)) ::
|
||||
("unk5" | bool) ::
|
||||
conditional(d != SquadRequestType.Invite, optional(bool, "unk6" | PacketHelpers.encodedWideStringAligned(6)))
|
||||
|
|
|
|||
|
|
@ -7,9 +7,27 @@ import scodec.{Attempt, Codec, Err}
|
|||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
||||
/**
|
||||
* Information about a specific squad member.
|
||||
* @param char_id the character's unique identifier
|
||||
* @param health the character's health value percentage, divided into 64 units
|
||||
* @param armor the character's armor value percentage, divided into 64 units
|
||||
* @param pos the world coordinates of the character
|
||||
* @param unk4 na;
|
||||
* usually, 2
|
||||
* @param unk5 na;
|
||||
* usually, 2
|
||||
* @param unk6 na;
|
||||
* usually, false
|
||||
* @param unk7 na
|
||||
* @param unk8 na;
|
||||
* if defined, will be defined with unk9
|
||||
* @param unk9 na;
|
||||
* if defined, will be defined with unk8
|
||||
*/
|
||||
final case class SquadStateInfo(char_id : Long,
|
||||
unk2 : Int,
|
||||
unk3 : Int,
|
||||
health : Int,
|
||||
armor : Int,
|
||||
pos : Vector3,
|
||||
unk4 : Int,
|
||||
unk5 : Int,
|
||||
|
|
@ -18,12 +36,19 @@ final case class SquadStateInfo(char_id : Long,
|
|||
unk8 : Option[Int],
|
||||
unk9 : Option[Boolean])
|
||||
|
||||
//7704001646c7a02810050a1a2bc97842280000
|
||||
// SquadState(PlanetSideGUID(4),List(SquadStateInfo(1684830722,64,64,Vector3(3152.1562,3045.0781,35.515625),2,2,false,0,None,None)))
|
||||
//770700342a28c028100420d60e9df8c2800000eab58a028100ce514b655ddc341400286d9130000021eb951539f4c4050800
|
||||
// SquadState(PlanetSideGUID(7),List(SquadStateInfo(1117948930,64,64,Vector3(2822.125,3919.0234,43.546875),0,0,false,0,None,None), SquadStateInfo(3937765890,64,64,Vector3(2856.3984,3882.3516,53.859375),0,0,false,416,None,None), SquadStateInfo(2262373120,0,0,Vector3(2909.0547,3740.539,67.296875),0,1,false,132,None,None)))
|
||||
//7704005dd9ccf01810132fdf9f9ef5c4a8000084de7a022c00e5898c8d5e4c429b004ed01d50181016395a4c364e08280001b901a070bd0140805308d59f90641f40000c001db11e280a00088d19e3a190f0ca6c0100
|
||||
// SquadState(PlanetSideGUID(4),List(SquadStateInfo(3718041345,64,64,Vector3(3966.5938,6095.8047,75.359375),2,2,false,0,None,None), SquadStateInfo(2229172738,22,0,Vector3(3268.4453,3690.3906,66.296875),2,2,false,728,None,None), SquadStateInfo(3976320257,64,64,Vector3(3530.6875,4635.1484,128.875),2,2,false,0,Some(441),Some(true)), SquadStateInfo(1088518658,64,64,Vector3(3336.3203,4601.3438,60.78125),2,2,false,0,Some(896),Some(true)), SquadStateInfo(1816627714,64,0,Vector3(5027.0625,4931.7734,48.234375),2,2,false,728,None,None)))
|
||||
/**
|
||||
* Dispatched by the server to update a squad member's representative icons on the continental maps and the interstellar map.<br>
|
||||
* <br>
|
||||
* This packet must be preceded by the correct protocol
|
||||
* to assign any character who is defined by `char_id` in `info_list`
|
||||
* as a member of this client's player's assigned squad by means of associating that said `char_id`.
|
||||
* The said preceding protocol also assigns the player's current zone (continent) and their ordinal position in the squad.
|
||||
* @see `SquadMemberEvent`
|
||||
* @param guid the squad's unique identifier;
|
||||
* must be consistent per packet on a given client;
|
||||
* does not have to be the global uid of the squad as according to the server
|
||||
* @param info_list information about the members in this squad who will be updated
|
||||
*/
|
||||
final case class SquadState(guid : PlanetSideGUID,
|
||||
info_list : List[SquadStateInfo])
|
||||
extends PlanetSideGamePacket {
|
||||
|
|
@ -43,8 +68,8 @@ object SquadStateInfo {
|
|||
object SquadState extends Marshallable[SquadState] {
|
||||
private val info_codec : Codec[SquadStateInfo] = (
|
||||
("char_id" | uint32L) ::
|
||||
("unk2" | uint(7)) ::
|
||||
("unk3" | uint(7)) ::
|
||||
("health" | uint(7)) ::
|
||||
("armor" | uint(7)) ::
|
||||
("pos" | Vector3.codec_pos) ::
|
||||
("unk4" | uint2) ::
|
||||
("unk5" | uint2) ::
|
||||
|
|
@ -56,14 +81,14 @@ object SquadState extends Marshallable[SquadState] {
|
|||
})
|
||||
).exmap[SquadStateInfo] (
|
||||
{
|
||||
case char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: _ :: u8 :: u9 :: HNil =>
|
||||
Attempt.Successful(SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, u8, u9))
|
||||
case char_id :: health :: armor :: pos :: u4 :: u5 :: u6 :: u7 :: _ :: u8 :: u9 :: HNil =>
|
||||
Attempt.Successful(SquadStateInfo(char_id, health, armor, pos, u4, u5, u6, u7, u8, u9))
|
||||
},
|
||||
{
|
||||
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, Some(u8), Some(u9)) =>
|
||||
Attempt.Successful(char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: true :: Some(u8) :: Some(u9) :: HNil)
|
||||
case SquadStateInfo(char_id, u2, u3, pos, u4, u5, u6, u7, None, None) =>
|
||||
Attempt.Successful(char_id :: u2 :: u3 :: pos :: u4 :: u5 :: u6 :: u7 :: false :: None :: None :: HNil)
|
||||
case SquadStateInfo(char_id, health, armor, pos, u4, u5, u6, u7, Some(u8), Some(u9)) =>
|
||||
Attempt.Successful(char_id :: health :: armor :: pos :: u4 :: u5 :: u6 :: u7 :: true :: Some(u8) :: Some(u9) :: HNil)
|
||||
case SquadStateInfo(char_id, health, armor, pos, u4, u5, u6, u7, None, None) =>
|
||||
Attempt.Successful(char_id :: health :: armor :: pos :: u4 :: u5 :: u6 :: u7 :: false :: None :: None :: HNil)
|
||||
case data @ (SquadStateInfo(_, _, _, _, _, _, _, _, Some(_), None) | SquadStateInfo(_, _, _, _, _, _, _, _, None, Some(_))) =>
|
||||
Attempt.Failure(Err(s"SquadStateInfo requires both unk8 and unk9 to be either defined or undefined at the same time - $data"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ package services.teamwork
|
|||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.SquadRequestType
|
||||
import net.psforever.types.{SquadRequestType, Vector3}
|
||||
|
||||
object SquadAction {
|
||||
trait Action
|
||||
|
||||
final case class Definition(player : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction) extends Action
|
||||
final case class Membership(request_type : SquadRequestType.Value, unk2 : Long, unk3 : Option[Long], player_name : String, unk5 : Option[Option[String]]) extends Action
|
||||
final case class Definition(player : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction)
|
||||
final case class Update(char_id : Long, health : Int, max_health : Int, armor : Int, max_armor : Int, pos : Vector3, zone_number : Int) extends Action
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ object SquadResponse {
|
|||
final case class Membership(request_type : SquadRequestType.Value, unk1 : Int, unk2 : Int, unk3 : Long, unk4 : Option[Long], player_name : String, unk5 : Boolean, unk6 : Option[Option[String]]) extends Response //see SquadMembershipResponse
|
||||
final case class Join(squad : Squad, positionsToUpdate : List[Int]) extends Response
|
||||
final case class Leave(squad : Squad, positionsToUpdate : List[(Long, Int)]) extends Response
|
||||
final case class UpdateMembers(squad : Squad, update_info : List[SquadAction.Update]) extends Response
|
||||
|
||||
final case class Detail(guid : PlanetSideGUID, leader : String, task : String, zone : PlanetSideZoneID, member_info : List[SquadPositionDetail]) extends Response
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package services.teamwork
|
|||
|
||||
import akka.actor.Actor
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.definition.converter.StatConverter
|
||||
import net.psforever.objects.teamwork.Squad
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.types.{PlanetSideEmpire, SquadRequestType, Vector3}
|
||||
|
|
@ -36,18 +37,26 @@ class SquadService extends Actor {
|
|||
testSquad.Membership(0).Name = "Wizkid45"
|
||||
testSquad.Membership(0).CharId = 30910985L
|
||||
testSquad.Membership(0).ZoneId = 5
|
||||
testSquad.Membership(0).Health = 64
|
||||
testSquad.Membership(0).Armor = 34
|
||||
testSquad.Membership(0).Position = Vector3(5526.5234f, 3818.7344f, 54.59375f)
|
||||
testSquad.Membership(1).Name = "xoBLADEox"
|
||||
testSquad.Membership(1).CharId = 42781919L
|
||||
testSquad.Membership(1).ZoneId = 5
|
||||
testSquad.Membership(1).Health = 54
|
||||
testSquad.Membership(1).Armor = 44
|
||||
testSquad.Membership(1).Position = Vector3(4673.5312f, 2604.8047f, 40.015625f)
|
||||
testSquad.Membership(3).Name = "cabal0428"
|
||||
testSquad.Membership(3).CharId = 353380L
|
||||
testSquad.Membership(3).ZoneId = 5
|
||||
testSquad.Membership(3).Health = 44
|
||||
testSquad.Membership(3).Armor = 54
|
||||
testSquad.Membership(3).Position = Vector3(4727.492f, 2613.5312f, 51.390625f)
|
||||
testSquad.Membership(4).Name = "xSkiku"
|
||||
testSquad.Membership(4).CharId = 41588340L
|
||||
testSquad.Membership(4).ZoneId = 5
|
||||
testSquad.Membership(4).Health = 34
|
||||
testSquad.Membership(4).Armor = 64
|
||||
testSquad.Membership(4).Position = Vector3(3675.0f, 4789.8047f, 63.21875f)
|
||||
idToSquad(PlanetSideGUID(3)) = testSquad
|
||||
testSquad.Listed = true
|
||||
|
|
@ -174,7 +183,7 @@ class SquadService extends Actor {
|
|||
case Service.Leave(None) | Service.LeaveAll() => ;
|
||||
|
||||
case SquadServiceMessage(tplayer, squad_action) => squad_action match {
|
||||
case SquadAction.Membership(request_type, char_id, optional_char_id, name, unk5) => request_type match {
|
||||
case SquadAction.Membership(request_type, char_id, optional_char_id, _, _) => request_type match {
|
||||
case SquadRequestType.Accept =>
|
||||
bids.get(char_id) match {
|
||||
case Some((squadGUID, line)) if idToSquad.get(squadGUID).nonEmpty =>
|
||||
|
|
@ -190,10 +199,18 @@ class SquadService extends Actor {
|
|||
position.Position = tplayer.Position
|
||||
position.ZoneId = 13
|
||||
memberToSquad(tplayer.Name) = squad
|
||||
//joining the squad
|
||||
sender ! SquadServiceResponse("", SquadResponse.Join(
|
||||
squad,
|
||||
squad.Membership.zipWithIndex.collect({ case (member, index) if member.CharId != 0 => index }).toList
|
||||
))
|
||||
//other squad members see us joining the squad
|
||||
val updatedIndex = List(line)
|
||||
squad.Membership
|
||||
.collect({ case member if member.CharId != 0 && member.CharId != char_id => member.Name })
|
||||
.foreach { name =>
|
||||
SquadEvents.publish( SquadServiceResponse(s"$name/Squad", SquadResponse.Join(squad, updatedIndex)) )
|
||||
}
|
||||
}
|
||||
bids.remove(char_id)
|
||||
case _ => ;
|
||||
|
|
@ -208,17 +225,66 @@ class SquadService extends Actor {
|
|||
val (member, index) = membership
|
||||
.find { case (_member, _) => _member.Name == name }
|
||||
.get
|
||||
val updateList = membership.collect({ case (_member, _) if _member.CharId > 0 => (_member.CharId, index) }).toList
|
||||
val updateList = membership.collect({ case (_member, _index) if _member.CharId > 0 => (_member.CharId, _index) }).toList
|
||||
memberToSquad.remove(name)
|
||||
member.Name = ""
|
||||
member.CharId = 0
|
||||
//leaving the squad completely
|
||||
sender ! SquadServiceResponse("", SquadResponse.Leave(squad, updateList))
|
||||
//other squad members see us leaving the squad
|
||||
val leavingMember = List((char_id, index))
|
||||
membership
|
||||
.collect({ case (_member, _) if _member.CharId > 0 => _member.Name })
|
||||
.foreach { name =>
|
||||
SquadEvents.publish( SquadServiceResponse(s"$name/Squad", SquadResponse.Leave(squad, leavingMember)) )
|
||||
}
|
||||
}
|
||||
|
||||
case _ => ;
|
||||
}
|
||||
|
||||
case SquadAction.Definition(tplayer : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, line : Int, action : SquadAction) =>
|
||||
case SquadAction.Update(char_id, health, max_health, armor, max_armor, pos, zone_number) =>
|
||||
memberToSquad.get(tplayer.Name) match {
|
||||
case Some(squad) =>
|
||||
squad.Membership.find(_.CharId == char_id) match {
|
||||
case Some(member) =>
|
||||
val newHealth = StatConverter.Health(health, max_health, min=1, max=64)
|
||||
val newArmor = StatConverter.Health(armor, max_armor, min=1, max=64)
|
||||
member.Health = newHealth
|
||||
member.Armor = newArmor
|
||||
member.Position = pos
|
||||
member.ZoneId = zone_number
|
||||
sender ! SquadServiceResponse("", SquadResponse.UpdateMembers(
|
||||
squad,
|
||||
squad.Membership
|
||||
.filterNot { _.CharId == 0 }
|
||||
.map { member => SquadAction.Update(member.CharId, member.Health, 0, member.Armor, 0, member.Position, member.ZoneId) }
|
||||
.toList
|
||||
))
|
||||
case _ => ;
|
||||
}
|
||||
// val (self, others) = squad.Membership.partition(_.CharId == char_id)
|
||||
// self match {
|
||||
// case Array(member) =>
|
||||
// val newHealth = StatConverter.Health(health, max_health, min=1, max=64)
|
||||
// val newArmor = StatConverter.Health(armor, max_armor, min=1, max=64)
|
||||
// member.Health = newHealth
|
||||
// member.Armor = newArmor
|
||||
// member.Position = pos
|
||||
// member.ZoneId = zone_number
|
||||
// sender ! SquadServiceResponse("", SquadResponse.UpdateMembers(
|
||||
// squad,
|
||||
// others
|
||||
// .map { member => SquadAction.Update(member.CharId, member.Health, 0, member.Armor, 0, member.Position, member.ZoneId) }
|
||||
// .toList
|
||||
// ))
|
||||
// case _ => ;
|
||||
// }
|
||||
|
||||
case None => ;
|
||||
}
|
||||
|
||||
case SquadAction.Definition(tplayer : Player, zone_ordinal_number : Int, guid : PlanetSideGUID, _ : Int, action : SquadAction) =>
|
||||
import net.psforever.packet.game.SquadAction._
|
||||
val squadOpt = GetParticipatingSquad(tplayer, zone_ordinal_number)
|
||||
action match {
|
||||
|
|
@ -320,11 +386,11 @@ class SquadService extends Actor {
|
|||
|
||||
case SelectRoleForYourself(position) =>
|
||||
//TODO need to ask permission from the squad leader, unless our character is the squad leader or already currently in the squad
|
||||
val name = tplayer.Name
|
||||
//val name = tplayer.Name
|
||||
squadOpt match {
|
||||
case Some(squad) if squad.GUID == guid =>
|
||||
//already a member of this squad; swap positions freely
|
||||
case Some(squad) =>
|
||||
case Some(_) =>
|
||||
//not a member of the requesting squad; do nothing
|
||||
case None =>
|
||||
//not a member of any squad; consider request of joining the target squad
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue