Implement some of 1.2 api and work on PlayerInfo in 1.3 api

This commit is contained in:
Aly 2018-12-03 20:59:22 -08:00
commit 3a9ea8de2a
No known key found for this signature in database
GPG key ID: 555B7346639DDAC3
15 changed files with 1074 additions and 0 deletions

720
.gitignore vendored Normal file
View file

@ -0,0 +1,720 @@
# Created by https://www.gitignore.io/api/vim,sbt,java,scala,emacs,ensime,eclipse,netbeans,jetbrains,visualstudio,jetbrains+iml,jetbrains+all,visualstudiocode
# Edit at https://www.gitignore.io/?templates=vim,sbt,java,scala,emacs,ensime,eclipse,netbeans,jetbrains,visualstudio,jetbrains+iml,jetbrains+all,visualstudiocode
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Eclipse Patch ###
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Annotation Processing
.apt_generated
.sts4-cache/
### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
dist/
# Flycheck
flycheck_*.el
# server auth directory
/server/
# projectiles files
.projectile
# directory configuration
.dir-locals.el
# network security
/network-security.data
### Ensime ###
# Ensime specific
.ensime
.ensime_cache/
.ensime_lucene/
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### JetBrains ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### JetBrains+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### JetBrains+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
### JetBrains+iml ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### JetBrains+iml Patch ###
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
### NetBeans ###
**/nbproject/private/
build/
nbbuild/
nbdist/
.nb-gradle/
### SBT ###
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
### Scala ###
### Vim ###
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp_proj
*_wpftmp.csproj
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# End of https://www.gitignore.io/api/vim,sbt,java,scala,emacs,ensime,eclipse,netbeans,jetbrains,visualstudio,jetbrains+iml,jetbrains+all,visualstudiocode

63
.scalafmt.conf Normal file
View file

@ -0,0 +1,63 @@
project {
includeFilters = [
".*.\\.scala$"
".*\\..sbt$"
]
}
maxColumn = 120
align = more
align {
openParenCallSite = false
openParenDefnSite = false
tokens = ["%", ":=", "~="]
}
assumeStandardLibraryStripMargin = true
includeCurlyBraceInSelectChains = false
continuationIndent {
callSite = 2
defnSite = 2
extendSite = 4
}
danglingParentheses = true
newlines {
alwaysBeforeTopLevelStatements = true
sometimesBeforeColonInMethodReturnType = true
penalizeSingleSelectMultiArgList = false
alwaysBeforeElseAfterCurlyIf = false
neverInResultType = false
}
spaces {
afterKeywordBeforeParen = true
}
binPack {
parentConstructors = true
literalArgumentLists = true
}
optIn {
breaksInsideChains = false
breakChainOnFirstMethodDot = true
configStyleArguments = true
}
runner {
optimizer {
forceConfigStyleOnOffset = 150
forceConfigStyleMinArgCount = 2
}
}
rewrite {
rules = [
SortImports
]
}

32
build.sbt Normal file
View file

@ -0,0 +1,32 @@
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
scalaVersion in ThisBuild := "2.11.12"
val sharedSettings = Seq(
name := "starry",
organization := "tf.bug",
version := "0.1.0",
scalaVersion := "2.11.12",
libraryDependencies ++= List(
"co.fs2" %%% "fs2-core" % "1.0.1",
"org.typelevel" %%% "spire" % "0.16.0",
"com.chuusai" %%% "shapeless" % "2.3.3",
"tf.bug" %%% "nose" % "0.1.0",
),
resolvers ++= Seq(
Resolver.sonatypeRepo("releases"),
Resolver.sonatypeRepo("snapshots"),
"jitpack" at "https://jitpack.io",
),
)
lazy val starry = crossProject(JSPlatform, JVMPlatform /*, NativePlatform */ )
.crossType(CrossType.Pure)
.settings(sharedSettings)
.jsSettings(crossScalaVersions := Seq("2.11.12", "2.12.7"))
.jvmSettings(crossScalaVersions := Seq("2.11.12", "2.12.7"))
// .nativeSettings(crossScalaVersions := Seq("2.11.12"))
lazy val starryJS = starry.js
lazy val starryJVM = starry.jvm
// lazy val starryNative = starry.native

1
project/build.properties Normal file
View file

@ -0,0 +1 @@
sbt.version = 1.2.7

4
project/plugins.sbt Normal file
View file

@ -0,0 +1,4 @@
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.0")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "0.6.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.25")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.8")

View file

@ -0,0 +1,76 @@
package tf.bug.starry
import scodec.bits.ByteVector
import shapeless.{::, HList, HNil, ProductTypeClass, ProductTypeClassCompanion}
import spire.math.UByte
import tf.bug.nose.{Color, ColorFormat}
trait Bytable[T] {
def apply(t: T): ByteVector
}
object Bytable extends BytableImplicits
trait BytableImplicits extends ProductTypeClassCompanion[Bytable] {
implicit class BytableOps[T](t: T)(implicit ev: Bytable[T]) {
def asBytes: ByteVector = ev(t)
}
implicit def messageAsBytable[P, M <: Message[P]]: Bytable[M] =
new Bytable[M] {
override def apply(t: M): ByteVector = {
import t.payloadEv
t.length.asBytes ++ t.id.asBytes ++ t.payload.asBytes
}
}
implicit def stringAsBytable: Bytable[String] = new Bytable[String] {
override def apply(t: String): ByteVector = {
ByteVector(t.getBytes("UTF-8"))
}
}
implicit def uByteAsBytable: Bytable[UByte] = new Bytable[UByte] {
override def apply(t: UByte): ByteVector = ByteVector.fromByte(t.signed)
}
implicit def intAsBytable: Bytable[Int] = new Bytable[Int] {
override def apply(t: Int): ByteVector = ByteVector.fromInt(t)
}
implicit def listAsBytable[A: Bytable]: Bytable[List[A]] =
new Bytable[List[A]] {
override def apply(t: List[A]): ByteVector =
t.map(_.asBytes).foldLeft(ByteVector.empty)(_ ++ _)
}
implicit def colorAsBytable: Bytable[Color] = new Bytable[Color] {
override def apply(t: Color): ByteVector = {
val f = t.rgb
List(f.red, f.green, f.blue).map(d => UByte((d * 255).toInt)).asBytes
}
}
object typeClass extends ProductTypeClass[Bytable] {
override def product[H, T <: HList](ch: Bytable[H], ct: Bytable[T]): Bytable[H :: T] =
new Bytable[H :: T] {
override def apply(t: H :: T): ByteVector = ch(t.head) ++ ct(t.tail)
}
override def emptyProduct: Bytable[HNil] = new Bytable[HNil] {
override def apply(t: HNil): ByteVector = ByteVector.empty
}
override def project[F, G](instance: => Bytable[G], to: F => G, from: G => F): Bytable[F] = new Bytable[F] {
override def apply(t: F): ByteVector = instance(to(t))
}
}
}

View file

@ -0,0 +1,15 @@
package tf.bug.starry
import implicits._
import spire.math.UByte
trait Message[P] {
implicit val payloadEv: Bytable[P] = implicitly[Bytable[P]]
val id: UByte
val payload: P
final val length: Int = payload.asBytes.size.toInt
}

View file

@ -0,0 +1,14 @@
package tf.bug.starry.client
import spire.math.UByte
import tf.bug.starry.Message
case class ConnectRequest(clientVersion: Int) extends Message[String] {
val fullIdentifier: String = s"Terraria$clientVersion"
override val id: UByte = UByte(0x01)
override val payload: String = fullIdentifier
}

View file

@ -0,0 +1,12 @@
package tf.bug.starry.client
import spire.math.UByte
import tf.bug.starry.Message
import tf.bug.starry.model.Player
case class PlayerInfo(p: Player) extends Message[(UByte, Player)] {
override val id: UByte = UByte(0x04)
override val payload: (UByte, Player) = (UByte(0), p)
}

View file

@ -0,0 +1,5 @@
package tf.bug.starry
import tf.bug.starry.model.NetworkTextImplicits
object implicits extends BytableImplicits with NetworkTextImplicits

View file

@ -0,0 +1,37 @@
package tf.bug.starry.model
import scodec.bits.ByteVector
import spire.math.UByte
import tf.bug.starry.implicits._
import tf.bug.starry.Bytable
import tf.bug.starry.model.NetworkText._
case class NetworkText(mode: Mode, content: String, substitutions: List[NetworkText]) {}
trait NetworkTextImplicits {
implicit def networkTextAsBytable: Bytable[NetworkText] =
new Bytable[NetworkText] {
override def apply(t: NetworkText): ByteVector =
t.mode.asBytes ++ t.content.asBytes ++ UByte(t.substitutions.size).asBytes ++ t.substitutions.asBytes
}
}
object NetworkText {
sealed trait Mode {
val value: UByte
}
object Mode {
case object Literal extends Mode {
override val value: UByte = UByte(0)
}
case object Formattable extends Mode {
override val value: UByte = UByte(1)
}
case object LocalizationKey extends Mode {
override val value: UByte = UByte(2)
}
}
}

View file

@ -0,0 +1,59 @@
package tf.bug.starry.model
import spire.math.UByte
import tf.bug.nose.Color
import tf.bug.starry.model.Player._
case class Player(
variant: Style,
hairStyle: UByte,
name: String,
// TODO: hair dye, visuals?, visuals 2?, misc?
hairColor: Color,
skinColor: Color,
eyeColor: Color,
shirtColor: Color,
undershirtColor: Color,
pantsColor: Color,
shoeColor: Color,
difficulty: Difficulty
)
object Player {
sealed trait Style {
val value: UByte
}
object Style {
case object Starter extends Style {
override val value: UByte = UByte(0)
}
case object Sticker extends Style {
override val value: UByte = UByte(1)
}
case object Gangster extends Style {
override val value: UByte = UByte(2)
}
case object Coat extends Style {
override val value: UByte = UByte(3)
}
case object Dress extends Style {
override val value: UByte = UByte(4)
}
}
sealed trait Difficulty {
val value: UByte
}
object Difficulty {
case object Softcore extends Difficulty {
override val value: UByte = UByte(0)
}
case object Mediumcore extends Difficulty {
override val value: UByte = UByte(1)
}
case object Hardcore extends Difficulty {
override val value: UByte = UByte(2)
}
}
}

View file

@ -0,0 +1,12 @@
package tf.bug.starry.server
import spire.math.UByte
import tf.bug.starry.Message
case class ContinueConnecting(newSlot: UByte) extends Message[UByte] {
override val id: UByte = UByte(0x03)
override val payload: UByte = newSlot
}

View file

@ -0,0 +1,12 @@
package tf.bug.starry.server
import spire.math.UByte
import tf.bug.starry.Message
case class Disconnect(reason: String) extends Message[String] {
override val id: UByte = UByte(0x02)
override val payload: String = reason
}

View file

@ -0,0 +1,12 @@
package tf.bug.starry.server
import spire.math.UByte
import tf.bug.starry.Message
import tf.bug.starry.model.Player
case class PlayerAppearance(slot: UByte, p: Player) extends Message[(UByte, Player)] {
override val id: UByte = UByte(0x04)
override val payload: (UByte, Player) = (slot, p)
}