mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
No Uniform, No Helmet, No Service (#1040)
* added extra checks to eliminate cosmetics from the packet transcoder where having them defined would be considered harmful to the data * new uniform options; moved cosmetics class file * assurance that the cosmetics settings are accurate during transitory points
This commit is contained in:
parent
fdcce870d9
commit
7e899e9ef3
|
|
@ -24,7 +24,6 @@ import net.psforever.objects.avatar.{
|
|||
BattleRank,
|
||||
Certification,
|
||||
Cooldowns,
|
||||
Cosmetic,
|
||||
Friend => AvatarFriend,
|
||||
Ignored => AvatarIgnored,
|
||||
Implant,
|
||||
|
|
@ -45,8 +44,9 @@ import net.psforever.objects.vital.HealFromImplant
|
|||
import net.psforever.packet.game.objectcreate.{ObjectClass, RibbonBars}
|
||||
import net.psforever.packet.game.{Friend => GameFriend, _}
|
||||
import net.psforever.types.{
|
||||
CharacterVoice,
|
||||
CharacterSex,
|
||||
CharacterVoice,
|
||||
Cosmetic,
|
||||
ExoSuitType,
|
||||
ImplantType,
|
||||
LoadoutType,
|
||||
|
|
@ -806,7 +806,13 @@ object AvatarActor {
|
|||
out.future
|
||||
}
|
||||
|
||||
def toAvatar(avatar: persistence.Avatar): Avatar =
|
||||
def toAvatar(avatar: persistence.Avatar): Avatar = {
|
||||
val bep = avatar.bep
|
||||
val convertedCosmetics = if (BattleRank.showCosmetics(bep)) {
|
||||
avatar.cosmetics.map(c => Cosmetic.valuesFromObjectCreateValue(c))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Avatar(
|
||||
avatar.id,
|
||||
BasicCharacterData(
|
||||
|
|
@ -816,10 +822,11 @@ object AvatarActor {
|
|||
avatar.headId,
|
||||
CharacterVoice(avatar.voiceId)
|
||||
),
|
||||
avatar.bep,
|
||||
bep,
|
||||
avatar.cep,
|
||||
decoration = ProgressDecoration(cosmetics = avatar.cosmetics.map(c => Cosmetic.valuesFromObjectCreateValue(c)))
|
||||
decoration = ProgressDecoration(cosmetics = convertedCosmetics)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AvatarActor(
|
||||
|
|
@ -936,6 +943,7 @@ class AvatarActor(
|
|||
case DeleteAvatar(id) =>
|
||||
import ctx._
|
||||
val performDeletion = for {
|
||||
_ <- ctx.run(query[persistence.Shortcut].filter(_.avatarId == lift(id)).delete)
|
||||
_ <- ctx.run(query[persistence.Implant].filter(_.avatarId == lift(id)).delete)
|
||||
_ <- ctx.run(query[persistence.Loadout].filter(_.avatarId == lift(id)).delete)
|
||||
_ <- ctx.run(query[persistence.Locker].filter(_.avatarId == lift(id)).delete)
|
||||
|
|
@ -1886,9 +1894,10 @@ class AvatarActor(
|
|||
)
|
||||
.onComplete {
|
||||
case Success(_) =>
|
||||
val zone = session.get.zone
|
||||
avatarCopy(avatar.copy(decoration = avatar.decoration.copy(cosmetics = Some(cosmetics))))
|
||||
session.get.zone.AvatarEvents ! AvatarServiceMessage(
|
||||
session.get.zone.id,
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(
|
||||
session.get.player.GUID,
|
||||
106,
|
||||
|
|
@ -2816,23 +2825,31 @@ class AvatarActor(
|
|||
|
||||
def setBep(bep: Long, modifier: ExperienceType): Unit = {
|
||||
import ctx._
|
||||
val current = BattleRank.withExperience(avatar.bep).value
|
||||
val next = BattleRank.withExperience(bep).value
|
||||
lazy val br24 = BattleRank.BR24.value
|
||||
val result = for {
|
||||
_ <-
|
||||
if (BattleRank.withExperience(bep).value < BattleRank.BR24.value) setCosmetics(Set())
|
||||
else Future.successful(())
|
||||
r <- ctx.run(query[persistence.Avatar].filter(_.id == lift(avatar.id)).update(_.bep -> lift(bep)))
|
||||
} yield r
|
||||
result.onComplete {
|
||||
case Success(_) =>
|
||||
val sess = session.get
|
||||
val zone = sess.zone
|
||||
val pguid = sess.player.GUID
|
||||
val zoneId = zone.id
|
||||
val events = zone.AvatarEvents
|
||||
val player = sess.player
|
||||
val pguid = player.GUID
|
||||
val localModifier = modifier
|
||||
sessionActor ! SessionActor.SendResponse(BattleExperienceMessage(pguid, bep, localModifier))
|
||||
zone.AvatarEvents ! AvatarServiceMessage(
|
||||
zone.id,
|
||||
AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep)
|
||||
)
|
||||
events ! AvatarServiceMessage(zoneId, AvatarAction.PlanetsideAttributeToAll(pguid, 17, bep))
|
||||
if (current < br24 && next >= br24 || current >= br24 && next < br24) {
|
||||
setCosmetics(Set()).onComplete { _ =>
|
||||
val evts = events
|
||||
val name = player.Name
|
||||
val guid = pguid
|
||||
evts ! AvatarServiceMessage(name, AvatarAction.PlanetsideAttributeToAll(guid, 106, 1)) //set to no helmet
|
||||
}
|
||||
}
|
||||
// when the level is reduced, take away any implants over the implant slot limit
|
||||
val implants = avatar.implants.zipWithIndex.map {
|
||||
case (implant, index) =>
|
||||
|
|
@ -2845,9 +2862,7 @@ class AvatarActor(
|
|||
)
|
||||
.onComplete {
|
||||
case Success(_) =>
|
||||
sessionActor ! SessionActor.SendResponse(
|
||||
AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0)
|
||||
)
|
||||
sessionActor ! SessionActor.SendResponse(AvatarImplantMessage(pguid, ImplantAction.Remove, index, 0))
|
||||
case Failure(exception) =>
|
||||
log.error(exception)("db failure")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import scala.concurrent.duration._
|
|||
import net.psforever.actors.zone.BuildingActor
|
||||
import net.psforever.login.WorldSession
|
||||
import net.psforever.objects.{Default, Player, Session}
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank, Cosmetic}
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification, CommandRank}
|
||||
import net.psforever.objects.serverobject.pad.{VehicleSpawnControl, VehicleSpawnPad}
|
||||
import net.psforever.objects.serverobject.resourcesilo.ResourceSilo
|
||||
import net.psforever.objects.serverobject.structures.{Amenity, Building}
|
||||
|
|
@ -30,7 +30,7 @@ import net.psforever.services.{CavernRotationService, InterstellarClusterService
|
|||
import net.psforever.services.chat.ChatService
|
||||
import net.psforever.services.chat.ChatService.ChatChannel
|
||||
import net.psforever.types.ChatMessageType.UNK_229
|
||||
import net.psforever.types.{ChatMessageType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.types.{ChatMessageType, Cosmetic, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.util.{Config, PointOfInterest}
|
||||
import net.psforever.zones.Zones
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package net.psforever.objects.avatar
|
||||
|
||||
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||
import net.psforever.packet.game.objectcreate.UniformStyle
|
||||
import net.psforever.types.UniformStyle
|
||||
|
||||
/** Battle ranks and their starting experience values
|
||||
* Source: http://wiki.psforever.net/wiki/Battle_Rank
|
||||
|
|
@ -19,9 +19,11 @@ sealed abstract class BattleRank(val value: Int, val experience: Long) extends I
|
|||
}
|
||||
}
|
||||
|
||||
def uniformStyle: UniformStyle.Value = {
|
||||
def uniformStyle: UniformStyle = {
|
||||
if (this.value >= BattleRank.BR25.value) {
|
||||
UniformStyle.ThirdUpgrade
|
||||
} else if (this.value == BattleRank.BR24.value) {
|
||||
UniformStyle.SecondUpgradeBR24
|
||||
} else if (this.value >= BattleRank.BR14.value) {
|
||||
UniformStyle.SecondUpgrade
|
||||
} else if (this.value >= BattleRank.BR7.value) {
|
||||
|
|
@ -30,7 +32,6 @@ sealed abstract class BattleRank(val value: Int, val experience: Long) extends I
|
|||
UniformStyle.Normal
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case object BattleRank extends IntEnum[BattleRank] {
|
||||
|
|
@ -103,4 +104,26 @@ case object BattleRank extends IntEnum[BattleRank] {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a number of battle experience points,
|
||||
* determine if the resulting battle rank is sufficient to display cosmetic details on a player character.
|
||||
* @see `BattleRank.withExperience`
|
||||
* @param bep amount of battle experience
|
||||
* @return `true`, if cosmetic elements will be visible;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def showCosmetics(bep: Long): Boolean = {
|
||||
showCosmetics(BattleRank.withExperience(bep).uniformStyle)
|
||||
}
|
||||
/**
|
||||
* Given a certain level of uniform dress corresponding to battle rank,
|
||||
* determine if the resulting battle rank is sufficient to display cosmetic details on a player character.
|
||||
* @param uniform the style of uniform
|
||||
* @return `true`, if cosmetic elements will be visible;
|
||||
* `false`, otherwise
|
||||
*/
|
||||
def showCosmetics(uniform: UniformStyle): Boolean = {
|
||||
uniform.value > UniformStyle.SecondUpgrade.value
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package net.psforever.objects.definition.converter
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.objects.avatar.BattleRank
|
||||
import net.psforever.objects.equipment.{Equipment, EquipmentSlot}
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.types.{ExoSuitType, GrenadeState, PlanetSideEmpire, PlanetSideGUID}
|
||||
|
|
@ -109,25 +110,41 @@ object AvatarConverter {
|
|||
}
|
||||
|
||||
def MakeCharacterData(obj: Player): (Boolean, Boolean) => CharacterData = {
|
||||
val avatar = obj.avatar
|
||||
val uniformStyle = avatar.br.uniformStyle
|
||||
val cosmetics = if (BattleRank.showCosmetics(uniformStyle)) {
|
||||
avatar.decoration.cosmetics
|
||||
} else {
|
||||
None
|
||||
}
|
||||
val MaxArmor = obj.MaxArmor
|
||||
CharacterData(
|
||||
StatConverter.Health(obj.Health, obj.MaxHealth),
|
||||
if (MaxArmor == 0) {
|
||||
val armor = if (MaxArmor == 0) {
|
||||
0
|
||||
} else {
|
||||
StatConverter.Health(obj.Armor, MaxArmor)
|
||||
},
|
||||
obj.avatar.br.uniformStyle,
|
||||
}
|
||||
CharacterData(
|
||||
StatConverter.Health(obj.Health, obj.MaxHealth),
|
||||
armor,
|
||||
uniformStyle,
|
||||
0,
|
||||
obj.avatar.cr.value,
|
||||
obj.avatar.implants.flatten.filter(_.active).flatMap(_.definition.implantType.effect).toList,
|
||||
obj.avatar.decoration.cosmetics
|
||||
avatar.cr.value,
|
||||
avatar.implants.flatten.filter(_.active).flatMap(_.definition.implantType.effect).toList,
|
||||
cosmetics
|
||||
)
|
||||
}
|
||||
|
||||
def MakeDetailedCharacterData(obj: Player): Option[Int] => DetailedCharacterData = {
|
||||
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) { Some(0L) }
|
||||
else { None }
|
||||
val maxOpt: Option[Long] = if (obj.ExoSuit == ExoSuitType.MAX) {
|
||||
Some(0L)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
val cosmetics = if (BattleRank.BR24.experience >= obj.avatar.bep) {
|
||||
obj.avatar.decoration.cosmetics
|
||||
} else {
|
||||
None
|
||||
}
|
||||
val ba: DetailedCharacterA = DetailedCharacterA(
|
||||
obj.avatar.bep,
|
||||
obj.avatar.cep,
|
||||
|
|
@ -164,7 +181,7 @@ object AvatarConverter {
|
|||
Nil,
|
||||
Nil,
|
||||
unkC = false,
|
||||
obj.avatar.decoration.cosmetics
|
||||
cosmetics
|
||||
)
|
||||
pad_length: Option[Int] => DetailedCharacterData(ba, bb(obj.avatar.bep, pad_length))(pad_length)
|
||||
}
|
||||
|
|
@ -229,7 +246,7 @@ object AvatarConverter {
|
|||
equip.GUID,
|
||||
5,
|
||||
DetailedLockerContainerData(
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, false, false, true, None, false, None, None, PlanetSideGUID(0)),
|
||||
CommonFieldData(PlanetSideEmpire.NEUTRAL, bops=false, alternate=false, v1=true, None, jammered=false, None, None, PlanetSideGUID(0)),
|
||||
None
|
||||
)
|
||||
))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.objects.avatar.Cosmetic
|
||||
import net.psforever.objects.avatar.BattleRank
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import net.psforever.types.{Cosmetic, UniformStyle}
|
||||
import scodec.codecs._
|
||||
import scodec.{Attempt, Codec, Err}
|
||||
import shapeless.{::, HNil}
|
||||
|
|
@ -26,24 +27,6 @@ object ImplantEffects extends Enumeration {
|
|||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint4L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Values for the four different color designs that impact a player's uniform.
|
||||
* Exo-suits get minor graphical updates at the following battle rank levels: seven (1), fourteen (2), and twenty-five (4).
|
||||
* The values 3 and 5 also exist and are visually descriptive to the third upgrade.
|
||||
*/
|
||||
object UniformStyle extends Enumeration {
|
||||
type Type = Value
|
||||
|
||||
val Normal = Value(0)
|
||||
val FirstUpgrade = Value(1)
|
||||
val SecondUpgrade = Value(2)
|
||||
val SecondUpgradeEx = Value(3)
|
||||
val ThirdUpgrade = Value(4)
|
||||
val ThirdUpgradeEx = Value(5)
|
||||
|
||||
implicit val codec = PacketHelpers.createEnumerationCodec(this, uint(3))
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a portion of an avatar's `ObjectCreateDetailedMessage` packet data.<br>
|
||||
* <br>
|
||||
|
|
@ -70,9 +53,9 @@ object UniformStyle extends Enumeration {
|
|||
* cosmetic armor associated with the command rank will be applied automatically
|
||||
* @param implant_effects the effects of implants that can be seen on a player's character;
|
||||
* the number of entries equates to the number of effects applied;
|
||||
* the maximu number of effects is three
|
||||
* the maximum number of effects is three
|
||||
* @param cosmetics optional decorative features that are added to the player's head model by console/chat commands;
|
||||
* they become available at battle rank 24, but here they require the third uniform upgrade (rank 25);
|
||||
* they become available at battle rank 24;
|
||||
* these flags do not exist if they are not applicable
|
||||
* @param is_backpack this player character should be depicted as a corpse;
|
||||
* corpses are either coffins (defunct), backpacks (normal), or a pastry (festive);
|
||||
|
|
@ -83,7 +66,7 @@ object UniformStyle extends Enumeration {
|
|||
final case class CharacterData(
|
||||
health: Int,
|
||||
armor: Int,
|
||||
uniform_upgrade: UniformStyle.Value,
|
||||
uniform_upgrade: UniformStyle,
|
||||
unk: Int,
|
||||
command_rank: Int,
|
||||
implant_effects: List[ImplantEffects.Value],
|
||||
|
|
@ -124,7 +107,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
def apply(
|
||||
health: Int,
|
||||
armor: Int,
|
||||
uniform: UniformStyle.Value,
|
||||
uniform: UniformStyle,
|
||||
cr: Int,
|
||||
implant_effects: List[ImplantEffects.Value],
|
||||
cosmetics: Option[Set[Cosmetic]]
|
||||
|
|
@ -139,7 +122,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||
("command_rank" | uintL(3)) ::
|
||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||
conditional(style.id > UniformStyle.SecondUpgrade.id, "cosmetics" | Cosmetic.codec)
|
||||
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
||||
})
|
||||
).exmap[CharacterData](
|
||||
{
|
||||
|
|
@ -178,7 +161,7 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
uint(3) :: //uniform_upgrade is actually interpreted as a 6u field, but the lower 3u seems to be discarded
|
||||
("command_rank" | uintL(3)) ::
|
||||
listOfN(uint2, "implant_effects" | ImplantEffects.codec) ::
|
||||
conditional(style.id > UniformStyle.SecondUpgrade.id, "cosmetics" | Cosmetic.codec)
|
||||
("cosmetics" | conditional(BattleRank.showCosmetics(style), Cosmetic.codec))
|
||||
}
|
||||
).exmap[CharacterData](
|
||||
{
|
||||
|
|
@ -200,7 +183,12 @@ object CharacterData extends Marshallable[CharacterData] {
|
|||
},
|
||||
{
|
||||
case CharacterData(_, _, uniform, unk, cr, implant_effects, cosmetics) =>
|
||||
Attempt.Successful(uniform :: unk :: cr :: implant_effects :: cosmetics :: HNil)
|
||||
val cos = if (BattleRank.showCosmetics(uniform)) {
|
||||
cosmetics.orElse(Some(Set[Cosmetic]()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Attempt.Successful(uniform :: unk :: cr :: implant_effects :: cos :: HNil)
|
||||
|
||||
case _ =>
|
||||
Attempt.Failure(Err("invalid character data; can not decode"))
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
package net.psforever.packet.game.objectcreate
|
||||
|
||||
import net.psforever.newcodecs.newcodecs
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification, Cosmetic}
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification}
|
||||
import net.psforever.packet.{Marshallable, PacketHelpers}
|
||||
import net.psforever.types.{ExoSuitType, ImplantType}
|
||||
import net.psforever.types.{Cosmetic, ExoSuitType, ImplantType}
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs._
|
||||
import shapeless.{::, HNil}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package net.psforever.objects.avatar
|
||||
package net.psforever.types
|
||||
|
||||
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||
import scodec.{Attempt, Codec}
|
||||
import scodec.codecs.uint
|
||||
import scodec.{Attempt, Codec}
|
||||
|
||||
/** Avatar cosmetic options */
|
||||
sealed abstract class Cosmetic(val value: Int) extends IntEnumEntry
|
||||
|
|
@ -58,9 +58,8 @@ case object Cosmetic extends IntEnum[Cosmetic] {
|
|||
}
|
||||
|
||||
/** Codec for object create messages */
|
||||
implicit val codec: Codec[Set[Cosmetic]] = uint(5).exmap(
|
||||
implicit val codec: Codec[Set[Cosmetic]] = uint(bits = 5).exmap(
|
||||
value => Attempt.Successful(Cosmetic.valuesFromObjectCreateValue(value)),
|
||||
cosmetics => Attempt.Successful(Cosmetic.valuesToObjectCreateValue(cosmetics))
|
||||
)
|
||||
|
||||
}
|
||||
28
src/main/scala/net/psforever/types/UniformStyle.scala
Normal file
28
src/main/scala/net/psforever/types/UniformStyle.scala
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package net.psforever.types
|
||||
|
||||
import enumeratum.values.{IntEnum, IntEnumEntry}
|
||||
import net.psforever.packet.PacketHelpers
|
||||
import scodec.codecs._
|
||||
import scodec.Codec
|
||||
|
||||
sealed abstract class UniformStyle(val value: Int) extends IntEnumEntry
|
||||
|
||||
/**
|
||||
* Values for the four different color designs that impact a player's uniform.
|
||||
* Exo-suits get minor graphical updates at the following battle rank levels: seven (1), fourteen (2), and twenty-five (4).
|
||||
* At battle rank twenty-four (3), the style does not update visually but switches to one suitable for display of cosmetics.
|
||||
* The design for value 5 is visually descriptive of the third upgrade.
|
||||
*/
|
||||
object UniformStyle extends IntEnum[UniformStyle] {
|
||||
val values: IndexedSeq[UniformStyle] = findValues
|
||||
|
||||
case object Normal extends UniformStyle(value = 0)
|
||||
case object FirstUpgrade extends UniformStyle(value = 1)
|
||||
case object SecondUpgrade extends UniformStyle(value = 2)
|
||||
case object SecondUpgradeBR24 extends UniformStyle(value = 3)
|
||||
case object ThirdUpgrade extends UniformStyle(value = 4)
|
||||
case object ThirdUpgradeEx extends UniformStyle(value = 5)
|
||||
|
||||
implicit val codec: Codec[UniformStyle] = PacketHelpers.createIntEnumCodec(this, uint(bits = 3))
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game.objectcreate
|
||||
|
||||
import net.psforever.objects.avatar.Cosmetic
|
||||
import net.psforever.packet.PacketCoding
|
||||
import net.psforever.packet.game.ObjectCreateMessage
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game.objectcreatedetailed
|
||||
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification, Cosmetic}
|
||||
import net.psforever.objects.avatar.{BattleRank, Certification}
|
||||
import org.specs2.mutable._
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.ObjectCreateDetailedMessage
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017 PSForever
|
||||
package game.objectcreatevehicle
|
||||
|
||||
import net.psforever.objects.avatar.Cosmetic
|
||||
import net.psforever.packet._
|
||||
import net.psforever.packet.game.objectcreate._
|
||||
import net.psforever.packet.game.ObjectCreateMessage
|
||||
|
|
|
|||
Loading…
Reference in a new issue