Skip to content
This repository was archived by the owner on Nov 28, 2025. It is now read-only.

Commit f18e70f

Browse files
authored
Merge pull request #199 from DockyardMC/fix/offline-mode
Rewrite Login sequence and add Velocity support
2 parents 5458f6c + 56e5403 commit f18e70f

35 files changed

+824
-354
lines changed

src/main/kotlin/io/github/dockyardmc/DockyardServer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.github.dockyardmc.implementations.commands.DefaultCommands
1313
import io.github.dockyardmc.npc.NpcCommand
1414
import io.github.dockyardmc.profiler.profiler
1515
import io.github.dockyardmc.protocol.NetworkCompression
16+
import io.github.dockyardmc.protocol.cryptography.EncryptionUtil
1617
import io.github.dockyardmc.protocol.packets.registry.ClientPacketRegistry
1718
import io.github.dockyardmc.protocol.packets.registry.ServerPacketRegistry
1819
import io.github.dockyardmc.registry.MinecraftVersions

src/main/kotlin/io/github/dockyardmc/config/Config.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.dockyardmc.config
22

33
import io.github.dockyardmc.DockyardServer
4+
import io.github.dockyardmc.protocol.proxy.VelocityProxy
45

56
class Config {
67
var ip: String = "0.0.0.0"
@@ -12,6 +13,10 @@ class Config {
1213
var implementationConfig: ImplementationConfig = ImplementationConfig()
1314
var updateChecker: Boolean = true
1415

16+
fun withVelocitySupport(secretKey: String) {
17+
VelocityProxy.enabled(secretKey)
18+
}
19+
1520
fun withUpdateChecker(updateChecker: Boolean) {
1621
this.updateChecker = updateChecker
1722
}

src/main/kotlin/io/github/dockyardmc/extentions/ExtendedMutableList.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import io.github.dockyardmc.item.ItemStack
77
import io.github.dockyardmc.location.Location
88
import io.github.dockyardmc.player.Player
99
import io.github.dockyardmc.player.Player.ChestAnimation
10-
import io.github.dockyardmc.player.setSkin
10+
//import io.github.dockyardmc.player.setSkin
1111
import io.github.dockyardmc.player.systems.GameMode
1212
import io.github.dockyardmc.protocol.packets.ClientboundPacket
1313
import io.github.dockyardmc.protocol.packets.play.clientbound.SoundCategory
@@ -129,13 +129,13 @@ fun Collection<Player>.setCanFly(canFly: Boolean) {
129129
this.toList().forEach { it.canFly.value = canFly }
130130
}
131131

132-
fun Collection<Player>.setSkin(uuid: UUID) {
133-
this.toList().forEach { it.setSkin(uuid) }
134-
}
135-
136-
fun Collection<Player>.setSkin(username: String) {
137-
this.toList().forEach { it.setSkin(username) }
138-
}
132+
//fun Collection<Player>.setSkin(uuid: UUID) {
133+
// this.toList().forEach { it.setSkin(uuid) }
134+
//}
135+
//
136+
//fun Collection<Player>.setSkin(username: String) {
137+
// this.toList().forEach { it.setSkin(username) }
138+
//}
139139

140140
fun Collection<Player>.setIsFlying(isFlying: Boolean) {
141141
this.toList().forEach { it.isFlying.value = isFlying }

src/main/kotlin/io/github/dockyardmc/implementations/commands/DefaultCommands.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class DefaultCommands : DefaultImplementationModule {
1818
ListCommand()
1919
EffectCommand()
2020
WeatherCommand()
21+
SkinCommand()
2122
if(InstrumentationUtils.isDebuggerAttached()) DebugCommands.register()
2223
}
2324
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.github.dockyardmc.implementations.commands
2+
3+
import io.github.dockyardmc.commands.Commands
4+
import io.github.dockyardmc.commands.PlayerArgument
5+
import io.github.dockyardmc.commands.StringArgument
6+
import io.github.dockyardmc.commands.simpleSuggestion
7+
import io.github.dockyardmc.player.Player
8+
import io.github.dockyardmc.player.SkinManager
9+
10+
class SkinCommand {
11+
init {
12+
Commands.add("/skin") {
13+
withDescription("Manages skins of players")
14+
withPermission("dockyard.commands.skin")
15+
16+
addSubcommand("set") {
17+
addArgument("player", PlayerArgument())
18+
addArgument("skin", StringArgument(), simpleSuggestion("<player username>"))
19+
execute { ctx ->
20+
val player = getArgument<Player>("player")
21+
val skin = getArgument<String>("skin")
22+
SkinManager.setSkinFromUsername(player, skin).thenAccept { success ->
23+
val message = if (success) "Successfully set your skin to skin of <white>$skin" else "<red>There was an error while setting your skin!"
24+
ctx.sendMessage(message)
25+
}
26+
}
27+
}
28+
}
29+
}
30+
}

src/main/kotlin/io/github/dockyardmc/item/ItemStackMeta.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import io.github.dockyardmc.attributes.AttributeModifier
44
import io.github.dockyardmc.data.DataComponent
55
import io.github.dockyardmc.data.DataComponentPatch
66
import io.github.dockyardmc.data.components.*
7-
import io.github.dockyardmc.player.ProfileProperty
87
import io.github.dockyardmc.protocol.types.ConsumeEffect
8+
import io.github.dockyardmc.protocol.types.GameProfile
99
import io.github.dockyardmc.protocol.types.ItemRarity
1010
import io.github.dockyardmc.registry.Items
1111
import io.github.dockyardmc.registry.Sounds
@@ -71,7 +71,7 @@ class ItemStackMeta {
7171
this.noxesiumImmovable = immovable
7272
}
7373

74-
fun withProfile(username: String? = null, uuid: UUID? = null, profile: ProfileProperty? = null) {
74+
fun withProfile(username: String? = null, uuid: UUID? = null, profile: GameProfile? = null) {
7575
if (material != Items.PLAYER_HEAD) {
7676
throw IllegalArgumentException("Item must be a player head")
7777
}
@@ -83,7 +83,7 @@ class ItemStackMeta {
8383
if (profile == null) {
8484
ProfileComponent(username, uuid, emptyList())
8585
} else {
86-
ProfileComponent(username, uuid, listOf(ProfileComponent.Property("textures", profile.value, profile.signature)))
86+
ProfileComponent(username, uuid, profile.properties.map { property -> ProfileComponent.Property(property.name, property.value, property.signature) })
8787
}
8888
)
8989
}
@@ -203,7 +203,7 @@ class ItemStackMeta {
203203
}
204204

205205
fun withAmount(amount: Int) {
206-
if(amount <= 0) this.amount = 1 else this.amount = amount
206+
if (amount <= 0) this.amount = 1 else this.amount = amount
207207
}
208208

209209
fun clearLore() {

src/main/kotlin/io/github/dockyardmc/npc/NpcCommand.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class NpcCommand {
104104
val name = getArgument<String>("name")
105105

106106
val npc = npcs[id] ?: throw CommandException("Npc with id $id does not exist!")
107-
npc.setSkin(name)
107+
// npc.setSkin(name)
108108
player.sendMessage("<lime>Set skin of npc <yellow>$id <lime>to <aqua>$name")
109109
}
110110
}

src/main/kotlin/io/github/dockyardmc/npc/PlayerNpc.kt

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.github.dockyardmc.extentions.sendPacket
99
import io.github.dockyardmc.location.Location
1010
import io.github.dockyardmc.player.*
1111
import io.github.dockyardmc.protocol.packets.play.clientbound.*
12+
import io.github.dockyardmc.protocol.types.GameProfile
1213
import io.github.dockyardmc.registry.EntityTypes
1314
import io.github.dockyardmc.registry.registries.EntityType
1415
import io.github.dockyardmc.utils.MojangUtil
@@ -31,13 +32,13 @@ class PlayerNpc(location: Location, username: String) : NpcEntity(location) {
3132
val username: Bindable<String> = Bindable(username)
3233
val isListed: Bindable<Boolean> = Bindable(false)
3334

34-
val profile: Bindable<ProfilePropertyMap?> = Bindable(null)
35+
val profile: Bindable<GameProfile?> = Bindable(null)
3536

3637
init {
3738

3839
isListed.valueChanged {
39-
val setListedUpdate = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
40-
viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(setListedUpdate))
40+
// val setListedUpdate = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
41+
// viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(setListedUpdate))
4142
}
4243

4344
displayedSkinParts.listUpdated {
@@ -50,18 +51,18 @@ class PlayerNpc(location: Location, username: String) : NpcEntity(location) {
5051
}
5152

5253
profile.valueChanged {
53-
val setListedUpdate = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
54-
val addPlayerUpdate =
55-
if (it.newValue != null) PlayerInfoUpdate(uuid, AddPlayerInfoUpdateAction(it.newValue!!)) else null
54+
// val setListedUpdate = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
55+
// val addPlayerUpdate =
56+
// if (it.newValue != null) PlayerInfoUpdate(uuid, AddPlayerInfoUpdateAction(it.newValue!!)) else null
5657

5758
viewers.sendPacket(ClientboundEntityRemovePacket(this))
5859
viewers.sendPacket(ClientboundPlayerInfoRemovePacket(uuid))
59-
if (addPlayerUpdate != null) viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(addPlayerUpdate))
60+
// if (addPlayerUpdate != null) viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(addPlayerUpdate))
6061

6162
viewers.sendPacket(
6263
ClientboundSpawnEntityPacket(id, uuid, type.getProtocolId(), location, location.yaw, 0, velocity)
6364
)
64-
viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(setListedUpdate))
65+
// viewers.sendPacket(ClientboundPlayerInfoUpdatePacket(setListedUpdate))
6566

6667
displayedSkinParts.triggerUpdate()
6768
equipment.triggerUpdate()
@@ -78,18 +79,18 @@ class PlayerNpc(location: Location, username: String) : NpcEntity(location) {
7879
override fun addViewer(player: Player): Boolean {
7980
if (!super.addViewer(player)) return false
8081

81-
val profileMap = if (profile.value == null) ProfilePropertyMap(username.value, mutableListOf()) else profile.value!!
82-
val infoUpdatePacket = PlayerInfoUpdate(uuid, AddPlayerInfoUpdateAction(profileMap))
83-
val listedPacket = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
84-
player.sendPacket(ClientboundPlayerInfoUpdatePacket(infoUpdatePacket))
85-
player.sendPacket(ClientboundPlayerInfoUpdatePacket(listedPacket))
82+
val profileMap = if (profile.value == null) GameProfile(uuid, username.value, mutableListOf()) else profile.value!!
83+
// val infoUpdatePacket = PlayerInfoUpdate(uuid, AddPlayerInfoUpdateAction(profileMap))
84+
// val listedPacket = PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))
85+
// player.sendPacket(ClientboundPlayerInfoUpdatePacket(infoUpdatePacket))
86+
// player.sendPacket(ClientboundPlayerInfoUpdatePacket(listedPacket))
8687

8788

8889
sendMetadataPacket(player)
8990
sendEquipmentPacket(player)
9091
sendPotionEffectsPacket(player)
9192

92-
if (profile.value == null) setSkin(username.value)
93+
// if (profile.value == null) setSkin(username.value)
9394
return true
9495
}
9596

@@ -98,21 +99,4 @@ class PlayerNpc(location: Location, username: String) : NpcEntity(location) {
9899
player.sendPacket(playerRemovePacket)
99100
super.removeViewer(player)
100101
}
101-
102-
fun setSkin(uuid: UUID) {
103-
world.scheduler.runAsync {
104-
val skin = MojangUtil.getSkinFromUUID(uuid)
105-
profile.value = ProfilePropertyMap(username.value, mutableListOf(skin))
106-
}
107-
}
108-
109-
fun setSkin(username: String) {
110-
var uuid: UUID? = null
111-
val asyncRunnable = world.scheduler.runAsync {
112-
uuid = MojangUtil.getUUIDFromUsername(username)
113-
}
114-
asyncRunnable.thenAccept {
115-
uuid?.let { setSkin(it) }
116-
}
117-
}
118102
}

src/main/kotlin/io/github/dockyardmc/player/Player.kt

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import io.github.dockyardmc.entity.metadata.EntityMetadata
1717
import io.github.dockyardmc.entity.metadata.EntityMetadataType
1818
import io.github.dockyardmc.events.*
1919
import io.github.dockyardmc.extentions.sendPacket
20-
import io.github.dockyardmc.inventory.ContainerInventory
2120
import io.github.dockyardmc.inventory.PlayerInventory
2221
import io.github.dockyardmc.inventory.give
2322
import io.github.dockyardmc.item.ItemStack
@@ -36,6 +35,7 @@ import io.github.dockyardmc.protocol.packets.play.clientbound.*
3635
import io.github.dockyardmc.protocol.packets.play.serverbound.ServerboundChatCommandPacket
3736
import io.github.dockyardmc.protocol.packets.play.serverbound.ServerboundClientInputPacket
3837
import io.github.dockyardmc.protocol.types.EquipmentSlot
38+
import io.github.dockyardmc.protocol.types.GameProfile
3939
import io.github.dockyardmc.registry.Blocks
4040
import io.github.dockyardmc.registry.EntityTypes
4141
import io.github.dockyardmc.registry.Items
@@ -78,7 +78,7 @@ class Player(
7878
override var inventorySize: Int = 35
7979

8080
var brand: String = "minecraft:vanilla"
81-
var profile: ProfilePropertyMap? = null
81+
lateinit var gameProfile: GameProfile
8282
var clientConfiguration: ClientConfiguration? = null
8383

8484
var isSneaking: Boolean = false
@@ -282,18 +282,20 @@ class Player(
282282

283283
override fun addViewer(player: Player): Boolean {
284284
if (player == this) return false
285-
val infoUpdatePacket = PlayerInfoUpdate(uuid, AddPlayerInfoUpdateAction(ProfilePropertyMap(username, mutableListOf(profile!!.properties[0]))))
286-
player.sendPacket(ClientboundPlayerInfoUpdatePacket(infoUpdatePacket))
287-
val namePacket = ClientboundPlayerInfoUpdatePacket(PlayerInfoUpdate(uuid, SetDisplayNameInfoUpdateAction(customName.value)))
288-
player.sendPacket(namePacket)
285+
286+
val updates = mutableListOf(
287+
PlayerInfoUpdate.AddPlayer(gameProfile),
288+
PlayerInfoUpdate.UpdateListed(isListed.value),
289+
PlayerInfoUpdate.UpdateDisplayName(customName.value),
290+
)
291+
player.sendPacket(ClientboundPlayerInfoUpdatePacket(mapOf(uuid to updates)))
289292

290293
if (!super.addViewer(player)) return false
291294

292295
player.sendMetadataPacket(this)
293296
this.displayedSkinParts.triggerUpdate()
294297
sendMetadataPacket(player)
295298
sendEquipmentPacket(player)
296-
player.sendPacket(ClientboundPlayerInfoUpdatePacket(PlayerInfoUpdate(uuid, SetListedInfoUpdateAction(isListed.value))))
297299
return true
298300
}
299301

@@ -384,6 +386,33 @@ class Player(
384386
}
385387
}
386388

389+
fun refreshGameProfileState() {
390+
val currentLocation = this.location
391+
392+
val removeInfo = ClientboundPlayerInfoRemovePacket(this)
393+
val entityRemovePacket = ClientboundEntityRemovePacket(this)
394+
val spawnEntityPacket = ClientboundSpawnEntityPacket(this.id, this.uuid, this.type.getProtocolId(), this.location, this.location.yaw, 0, this.velocity)
395+
val updates = mutableListOf(
396+
PlayerInfoUpdate.AddPlayer(this.gameProfile),
397+
PlayerInfoUpdate.UpdateListed(this.isListed.value),
398+
PlayerInfoUpdate.UpdateDisplayName(this.customName.value),
399+
)
400+
val addPacket = ClientboundPlayerInfoUpdatePacket(mapOf(this.uuid to updates.toList()))
401+
402+
this.sendPacket(removeInfo)
403+
this.sendPacket(entityRemovePacket)
404+
this.sendPacket(addPacket)
405+
this.respawn(false)
406+
407+
viewers.sendPacket(removeInfo)
408+
viewers.sendPacket(entityRemovePacket)
409+
viewers.sendPacket(addPacket)
410+
viewers.sendPacket(spawnEntityPacket)
411+
412+
this.displayedSkinParts.triggerUpdate()
413+
this.teleport(currentLocation)
414+
}
415+
387416
override fun teleport(location: Location) {
388417
if (!WorldManager.worlds.containsValue(location.world)) throw Exception("That world does not exist!")
389418
if (location.world != world) location.world.join(this, location)
@@ -460,7 +489,7 @@ class Player(
460489
}
461490

462491
fun closeInventory() {
463-
if(currentlyOpenScreen != null) currentlyOpenScreen!!.dispose()
492+
if (currentlyOpenScreen != null) currentlyOpenScreen!!.dispose()
464493

465494
sendPacket(ClientboundCloseInventoryPacket(0))
466495
sendPacket(ClientboundCloseInventoryPacket(1))

0 commit comments

Comments
 (0)