Skip to content

Commit 2bbc87e

Browse files
authored
Merge pull request #50 from DockyardMC/feature/multiplayer
Feature/multiplayer
2 parents 6576bd4 + 60da230 commit 2bbc87e

37 files changed

+605
-97
lines changed

.idea/kotlinc.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle.kts

+55
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import java.io.IOException
2+
import java.net.URI
3+
import java.net.http.HttpClient
4+
import java.net.http.HttpRequest
5+
import java.net.http.HttpResponse
26

37
plugins {
48
`maven-publish`
@@ -115,4 +119,55 @@ publishing {
115119
from(components["java"])
116120
}
117121
}
122+
}
123+
124+
tasks.publish {
125+
finalizedBy("sendPublishWebhook")
126+
}
127+
128+
task("sendPublishWebhook") {
129+
group = "publishing"
130+
description = "Sends a webhook message after publishing to Maven."
131+
132+
doLast {
133+
sendWebhookToDiscord(System.getenv("DISCORD_DOCKYARD_WEBHOOK"))
134+
}
135+
}
136+
137+
fun sendWebhookToDiscord(webhookUrl: String) {
138+
val httpClient = HttpClient.newHttpClient()
139+
140+
val requestBody = embed()
141+
val request = HttpRequest.newBuilder()
142+
.uri(URI(webhookUrl))
143+
.header("Content-Type", "application/json")
144+
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
145+
.build()
146+
147+
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
148+
149+
.thenRun { println("Webhook sent successfully!") }
150+
.exceptionally { throwable ->
151+
throwable.printStackTrace()
152+
null
153+
}
154+
}
155+
156+
157+
fun embed(): String {
158+
return """
159+
{
160+
"content": null,
161+
"embeds": [
162+
{
163+
"title": "Published to Maven",
164+
"description": "`io.github.dockyardmc:dockyard:$dockyardVersion` was successfully published to maven!",
165+
"color": 5046022
166+
}
167+
],
168+
"username": "Mavenboo",
169+
"avatar_url": "https://storage.moemate.io/9edcfd27fd20abe29e93bf904f633d61b4fccadc/3f1c4383-1ba3-43f9-891e-f6a96abbe970.webp",
170+
"attachments": []
171+
}
172+
""".trimIndent()
118173
}

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
dockyard.version=0.4
1+
dockyard.version=0.5
22
kotlin.code.style=official

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

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
package io.github.dockyardmc
22

3-
import io.github.dockyardmc.commands.*
3+
import io.github.dockyardmc.commands.Commands
4+
import io.github.dockyardmc.commands.PlayerArgument
45
import io.github.dockyardmc.datagen.EventsDocumentationGenerator
56
import io.github.dockyardmc.datagen.VerifyPacketIds
67
import io.github.dockyardmc.events.Events
8+
import io.github.dockyardmc.events.PlayerDropItemEvent
79
import io.github.dockyardmc.events.PlayerJoinEvent
810
import io.github.dockyardmc.events.PlayerLeaveEvent
911
import io.github.dockyardmc.extentions.broadcastMessage
10-
import io.github.dockyardmc.location.Location
12+
import io.github.dockyardmc.item.ItemStack
1113
import io.github.dockyardmc.particles.spawnParticle
12-
import io.github.dockyardmc.player.*
14+
import io.github.dockyardmc.player.GameMode
15+
import io.github.dockyardmc.player.Player
16+
import io.github.dockyardmc.player.add
17+
import io.github.dockyardmc.player.toPersistent
18+
import io.github.dockyardmc.protocol.packets.play.clientbound.EntityEquipmentLayer
19+
import io.github.dockyardmc.protocol.packets.play.serverbound.placementRules
1320
import io.github.dockyardmc.registry.*
1421
import io.github.dockyardmc.sounds.playSound
1522
import io.github.dockyardmc.utils.DebugScoreboard
1623
import io.github.dockyardmc.utils.MathUtils
1724
import io.github.dockyardmc.utils.Vector3f
18-
import io.github.dockyardmc.world.World
19-
import io.github.dockyardmc.world.WorldManager
2025

2126
// This is just testing/development environment.
2227
// To properly use dockyard, visit https://dockyardmc.github.io/Wiki/wiki/quick-start.html
@@ -51,22 +56,25 @@ fun main(args: Array<String>) {
5156
}
5257

5358

54-
Commands.add("/explode") {
59+
Commands.add("/equipment") {
5560
addArgument("player", PlayerArgument())
56-
withPermission("player.admin")
57-
withDescription("executes stuff")
5861
execute {
59-
val executingPlayer = it.getPlayerOrThrow()
60-
val player = getArgument<Player>("player")
62+
val player = it.getPlayerOrThrow()
63+
val target = getArgument<Player>("player")
6164

62-
player.spawnParticle(player.location, Particles.EXPLOSION_EMITTER, Vector3f(1f), amount = 5)
63-
player.playSound("minecraft:entity.generic.explode", volume = 2f, pitch = MathUtils.randomFloat(0.6f, 1.3f))
64-
65-
player.sendMessage("<yellow>You got <rainbow><b>totally exploded <yellow>by <red>$executingPlayer")
66-
executingPlayer.sendMessage("<yellow>You <rainbow><b>totally exploded <yellow>player <red>$player")
65+
target.equipmentLayers[player.toPersistent()] = EntityEquipmentLayer(
66+
helmet = ItemStack(Items.LIME_STAINED_GLASS),
67+
offHand = ItemStack(Items.LEAD),
68+
boots = ItemStack(Items.NETHERITE_BOOTS)
69+
)
6770
}
6871
}
6972

73+
Events.on<PlayerDropItemEvent> {
74+
it.cancelled = true
75+
}
76+
77+
7078
val server = DockyardServer()
7179
server.start()
7280
}

src/main/kotlin/io/github/dockyardmc/blocks/GeneralBlockPlacementRules.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object GeneralBlockPlacementRules {
1717
val existingBlock = world.getBlock(originalClickedBlock)
1818
val placementLocation = world.getBlock(where)
1919

20-
if(placementLocation != Blocks.AIR) canBePlaced = CancelReason(false, "Block at new location is not air")
20+
if(placementLocation != Blocks.AIR && placementLocation != Blocks.LIGHT) canBePlaced = CancelReason(false, "Block at new location is not air")
2121
if(isLocationInsideBoundingBox(where, placer.world.entities.values) && newBlock.boundingBox == "block") canBePlaced = CancelReason(false, "Block collides with entity")
2222
if(world.getBlock(originalClickedBlock).boundingBox != "block") canBePlaced = CancelReason(false, "Block is not full block")
2323
if(existingBlock.isClickable && !placer.isSneaking) canBePlaced = CancelReason(false, "Block is clickable and player is not sneaking")

src/main/kotlin/io/github/dockyardmc/commands/CommandHandler.kt

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ object CommandHandler {
9090
val argumentData = command.arguments.values.toList()[index - 1]
9191

9292
argumentData.returnedValue = when(argumentData.expectedReturnValueType) {
93+
Boolean::class -> value == "true"
9394
String::class -> value
9495
Player::class -> PlayerManager.players.firstOrNull { it.username == value } ?: throw CommandException("Player $value is not online or the supplied name is invalid!")
9596
Int::class -> value.toIntOrNull() ?: throw CommandException("\"$value\" is not of type Int")

src/main/kotlin/io/github/dockyardmc/commands/SuggestionHandler.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package io.github.dockyardmc.commands
55
import io.github.dockyardmc.player.Player
66
import io.github.dockyardmc.protocol.packets.play.clientbound.ClientboundSuggestionsResponse
77
import io.github.dockyardmc.utils.getEnumEntries
8+
import io.github.dockyardmc.world.WorldManager
89
import kotlin.reflect.KClass
910

1011
object SuggestionHandler {
@@ -29,9 +30,16 @@ object SuggestionHandler {
2930

3031
fun handleSuggestion(current: CommandArgumentData, inputCommand: String, currentlyTyped: String, player: Player, transactionId: Int) {
3132
// Auto suggest enum entries if user defined suggestion is null
32-
if(current.argument is EnumArgument && current.suggestions == null) {
33-
val enum = current.argument.enumType as KClass<Enum<*>>
34-
current.suggestions = SuggestionProvider.withContext { getEnumEntries(enum).map { it.name.lowercase() } }
33+
if(current.suggestions == null) {
34+
when(current.argument ) {
35+
is EnumArgument -> {
36+
val enum = current.argument.enumType as KClass<Enum<*>>
37+
current.suggestions = SuggestionProvider.withContext { getEnumEntries(enum).map { it.name.lowercase() } }
38+
}
39+
is WorldArgument -> {
40+
current.suggestions = SuggestionProvider.simple(WorldManager.worlds.keys.toList())
41+
}
42+
}
3543
}
3644

3745
val suggestions = current.suggestions ?: return

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ data class ServerConfig(
6969
val port: Int = 25565,
7070
val networkCompressionThreshold: Int = 256,
7171
val cacheSchematics: Boolean = true,
72-
val debug: Boolean = false
72+
val debug: Boolean = false,
73+
val maxPlayers: Int = 50
7374
)
7475

7576
@Serializable

src/main/kotlin/io/github/dockyardmc/entities/Entity.kt

+20
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,25 @@ abstract class Entity(open var location: Location, open var world: World) {
4646
val team: Bindable<Team?> = Bindable(null)
4747
val isOnFire: Bindable<Boolean> = Bindable(false)
4848
val freezeTicks: Bindable<Int> = Bindable(0)
49+
val equipment: Bindable<EntityEquipment> = Bindable(EntityEquipment())
50+
val equipmentLayers: BindableMap<PersistentPlayer, EntityEquipmentLayer> = BindableMap()
4951

5052
constructor(location: Location): this(location, location.world)
5153

5254
init {
5355

56+
equipment.valueChanged { viewers.forEach(::sendEquipmentPacket) }
57+
58+
equipmentLayers.itemSet {
59+
val player = it.key.toPlayer()
60+
if(player != null) sendEquipmentPacket(player)
61+
}
62+
63+
equipmentLayers.itemRemoved {
64+
val player = it.key.toPlayer()
65+
if(player != null) sendEquipmentPacket(player)
66+
}
67+
5468
isOnFire.valueChanged {
5569
val meta = getEntityMetadataState(this) {
5670
isOnFire = it.newValue
@@ -180,6 +194,12 @@ abstract class Entity(open var location: Location, open var world: World) {
180194
player.sendPacket(packet)
181195
}
182196

197+
open fun sendEquipmentPacket(player: Player) {
198+
val equipment = getMergedEquipmentData(equipment.value, equipmentLayers[player.toPersistent()])
199+
val packet = ClientboundSetEquipmentPacket(this, equipment)
200+
player.sendPacket(packet)
201+
}
202+
183203
open fun calculateBoundingBox(): BoundingBox {
184204
val width = type.width
185205
val height = type.height
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.github.dockyardmc.events
2+
3+
import io.github.dockyardmc.annotations.EventDocumentation
4+
import io.github.dockyardmc.item.ItemStack
5+
import io.github.dockyardmc.player.Player
6+
7+
@EventDocumentation("when player drops item", true)
8+
class PlayerDropItemEvent(val player: Player, var itemStack: ItemStack): CancellableEvent() {
9+
}

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

+7
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,11 @@ fun Int.toBoolean(): Boolean {
1313
0 -> false
1414
else -> true
1515
}
16+
}
17+
18+
fun Boolean.toScrollText(): String {
19+
return when(this) {
20+
true -> "<lime>true"
21+
else -> "<red>false"
22+
}
1623
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.github.dockyardmc.implementations.commands
2+
3+
import io.github.dockyardmc.commands.Commands
4+
import io.github.dockyardmc.commands.WorldArgument
5+
import io.github.dockyardmc.world.World
6+
7+
class DebugCommands {
8+
9+
init {
10+
Commands.add("chunks") {
11+
addArgument("world", WorldArgument())
12+
execute {
13+
val player = it.getPlayerOrThrow()
14+
val world = getArgument<World>("world")
15+
val message = buildString {
16+
appendLine("<lime>Chunks in memory: <aqua>${world.chunks.size}")
17+
appendLine("<lime>Surface: <aqua>${player.location.getChunk()!!.worldSurface}")
18+
appendLine("<lime>Motion blocking: <aqua>${player.location.getChunk()!!.motionBlocking}")
19+
appendLine("<lime>Index: <aqua>${player.location.getChunk()!!.getIndex()}")
20+
}
21+
it.sendMessage(message)
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package io.github.dockyardmc.implementations.commands
22

3+
import io.github.dockyardmc.config.ConfigManager
4+
35
class DockyardCommands {
46

57
init {
6-
78
GamemodeCommand()
89
VersionAndHelpCommand()
910
WorldCommand()
1011
SoundCommand()
1112
GiveCommand()
12-
13+
TeleportCommand()
14+
SchematicCommand()
15+
TimeCommand()
16+
if(ConfigManager.currentConfig.serverConfig.debug) {
17+
ViewerCommand()
18+
DebugCommands()
19+
}
1320
}
1421
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.github.dockyardmc.implementations.commands
2+
3+
import io.github.dockyardmc.commands.Commands
4+
import io.github.dockyardmc.commands.StringArgument
5+
import io.github.dockyardmc.commands.SuggestionProvider
6+
import io.github.dockyardmc.config.ConfigManager
7+
import io.github.dockyardmc.schematics.SchematicReader
8+
import io.github.dockyardmc.schematics.placeSchematic
9+
import java.io.File
10+
11+
class SchematicCommand {
12+
init {
13+
Commands.add("/schematic") {
14+
withPermission("dockyard.commands.schematic")
15+
16+
addSubcommand("place") {
17+
addArgument("path", StringArgument(), SuggestionProvider.simple("<path to schematic file>"))
18+
execute {
19+
val player = it.getPlayerOrThrow()
20+
val path = getArgument<String>("path")
21+
val start = System.currentTimeMillis()
22+
player.world.placeSchematic {
23+
schematic = SchematicReader.read(File(path))
24+
location = player.location
25+
then = {
26+
val end = System.currentTimeMillis()
27+
player.sendMessage("<gray>Schematic pasted in <lime>${end - start}ms")
28+
}
29+
}
30+
}
31+
}
32+
33+
if(ConfigManager.currentConfig.serverConfig.cacheSchematics) {
34+
addSubcommand("cache") {
35+
execute {
36+
val message = buildString {
37+
append("<gray>Current schematic cache:\n")
38+
SchematicReader.cache.forEach { schem ->
39+
appendLine("<gray>- <lime>${schem.key.subSequence(0, 16)} <dark_gray>- <aqua>(${schem.value.blocks.size} bytes, ${schem.value.pallete.values.size} blocks in pellet)")
40+
}
41+
}
42+
it.sendMessage(message)
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)