Skip to content

Commit 2e75136

Browse files
Hooks work! (netcode untested)
1 parent 8f07dd3 commit 2e75136

24 files changed

+516
-131
lines changed

netcode.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Netcode
2+
3+
I'll attempt to describe how the netcode works here, mostly for my own reference later. In server communication I will
4+
use the term "the client" to refer to the player whose hooks we're processing, and "other clients" to refer to other
5+
players in the vicinity. I will also use the term "canonical" to refer to the state on the server, since the server is
6+
king.
7+
8+
## Syncing
9+
10+
On the client, so long as the syncing is up to date, the player's own hooks are always in the canonical order. However,
11+
other players' hooks may not be in canonical order. For this reason controllers are only ticked on the client for the
12+
client's player. Other players' hooks will still move and hit blocks on their own, but the controller will not be
13+
invoked for them.
14+
15+
## Firing a hook
16+
17+
To fire a hook:
18+
19+
- **[Client]** Adds a hook to the client-side `HookedPlayerData` with a random UUID [1]
20+
- **[Client]** Sends a `FireHookPacket` that contains the firing parameters (start position + direction) to the server
21+
- **[Server]** Receives the `FireHookPacket` and performs some validation on the inputs
22+
- **[Server]** If the packet fails validation,
23+
- **[Server]** The full `HookedPlayerData` is sent to the client in a `SyncHookedDataPacket` to correct the discrepancy
24+
- **[Server]** If the packet succeeds validation,
25+
- **[Server]** The server adds a hook to the server-side `HookedPlayerData` with a random UUID
26+
- **[Server]** The full `HookedPlayerData` is sent back to the player in a `SyncHookedDataPacket`, ensuring that
27+
everything on the client is exactly up to date.
28+
- **[Server]** A `SyncHookPacket` is sent to other clients
29+
30+
[1]: The client-side hook with this temporary UUID will be seamlessly replaced by the server-side hook with the
31+
canonical UUID

src/generated/resources/.cache/cache

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
d5ad42510969279aea44a0b2019f1498716dc852 assets/hooked/models/item/diamond_hook.json
2+
60e9f401cbf0bda316cc2509b9da561f31488bac assets/hooked/models/item/ender_hook.json
13
24cf6a03abc3fa15e34d2e1519249e535b423acd assets/hooked/models/item/iron_hook.json
2-
a294c4eba37684d7a375d14d165038df0b9c02d4 data/curios/tags/items/hooked.json
4+
7a73fe9d10afdb403d96cb483c1d559e8bb8fa00 assets/hooked/models/item/wood_hook.json
5+
84587976d102955f8ef61a2bd3d3f5174eb82d61 data/curios/tags/items/hooked.json
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"parent": "item/generated",
3+
"textures": {
4+
"layer0": "hooked:item/diamond_hook"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"parent": "item/generated",
3+
"textures": {
4+
"layer0": "hooked:item/ender_hook"
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"parent": "item/generated",
3+
"textures": {
4+
"layer0": "hooked:item/wood_hook"
5+
}
6+
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"replace": false,
33
"values": [
4-
"hooked:iron_hook"
4+
"hooked:wood_hook",
5+
"hooked:iron_hook",
6+
"hooked:diamond_hook",
7+
"hooked:ender_hook"
58
]
69
}

src/main/kotlin/dev/thecodewarrior/hooked/HookedMod.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import com.teamwizardry.librarianlib.foundation.BaseMod
66
import com.teamwizardry.librarianlib.foundation.util.TagWrappers
77
import dev.thecodewarrior.hooked.client.HookRenderManager
88
import dev.thecodewarrior.hooked.client.Keybinds
9-
import dev.thecodewarrior.hooked.hook.processor.CommonHookProcessor
9+
import dev.thecodewarrior.hooked.hook.processor.ClientHookProcessor
10+
import dev.thecodewarrior.hooked.hook.processor.ServerHookProcessor
1011
import dev.thecodewarrior.hooked.hook.type.HookType
12+
import dev.thecodewarrior.hooked.network.BasicHookJumpPacket
1113
import dev.thecodewarrior.hooked.network.FireHookPacket
14+
import dev.thecodewarrior.hooked.network.SyncHookDataPacket
15+
import dev.thecodewarrior.hooked.network.SyncIndividualHooksPacket
1216
import net.minecraft.inventory.container.PlayerContainer
1317
import net.minecraft.util.Tuple
1418
import net.minecraftforge.client.event.TextureStitchEvent
@@ -34,18 +38,22 @@ object HookedMod: BaseMod(true) {
3438
HookedModItems.registerItemDatagen(registrationManager)
3539
HookedModCapabilities.registerCapabilities(registrationManager)
3640
eventBus.register(HookedModHookTypes)
37-
courier.registerCourierPacket<FireHookPacket>(NetworkDirection.PLAY_TO_SERVER)
38-
3941
proxy = SidedSupplier.sided({ HookedClientProxy }, { HookedDedicatedServerProxy })
42+
43+
courier.registerCourierPacket<FireHookPacket>(NetworkDirection.PLAY_TO_SERVER)
44+
courier.registerCourierPacket<SyncIndividualHooksPacket>(NetworkDirection.PLAY_TO_CLIENT)
45+
courier.registerCourierPacket<SyncHookDataPacket>(NetworkDirection.PLAY_TO_CLIENT)
46+
courier.registerCourierPacket<BasicHookJumpPacket>(NetworkDirection.PLAY_TO_SERVER)
4047
}
4148

4249
override fun clientSetup(e: FMLClientSetupEvent) {
4350
ClientRegistry.registerKeyBinding(Keybinds.fireKey)
44-
HookRenderManager // registers itself
51+
HookRenderManager // registers itself for events
52+
ClientHookProcessor // registers itself for events
4553
}
4654

4755
override fun commonSetup(e: FMLCommonSetupEvent) {
48-
CommonHookProcessor // registers itself
56+
ServerHookProcessor // registers itself for events
4957
}
5058

5159
override fun interModCommsEnqueue(e: InterModEnqueueEvent) {
@@ -70,6 +78,7 @@ object HookedMod: BaseMod(true) {
7078
RegistryBuilder<HookType>()
7179
.setName(loc("hooked:hook_type"))
7280
.setType(HookType::class.java)
81+
.setDefaultKey(loc("hooked:none"))
7382
.create()
7483
}
7584
}

src/main/kotlin/dev/thecodewarrior/hooked/HookedModHookTypes.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ import net.minecraftforge.event.RegistryEvent
77
import net.minecraftforge.eventbus.api.SubscribeEvent
88

99
object HookedModHookTypes {
10-
val iron = BasicHookType(2, 16.0, 0.8, 0.4, 0.5, 0.05).also {
11-
it.registryName = loc("hooked:iron_hook")
12-
}
10+
val types: List<HookType> = listOf(
11+
BasicHookType(1, 8.0, 0.4, 0.5, 0.2, 0.05).also { it.registryName = loc("hooked:wood_hook") },
12+
BasicHookType(2, 16.0, 0.8, 0.5, 0.4, 0.05).also { it.registryName = loc("hooked:iron_hook") },
13+
BasicHookType(4, 24.0, 1.2, 0.5, 1.0, 0.05).also { it.registryName = loc("hooked:diamond_hook") },
14+
BasicHookType(1, 64.0, 64.0, 0.5, 2.25, 0.05).also { it.registryName = loc("hooked:ender_hook") },
15+
)
1316

1417
@SubscribeEvent
1518
fun registerTypes(e: RegistryEvent.Register<HookType>) {
16-
e.registry.register(iron)
19+
e.registry.register(HookType.NONE)
20+
types.forEach {
21+
e.registry.register(it)
22+
}
1723
}
1824
}

src/main/kotlin/dev/thecodewarrior/hooked/HookedModItems.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@ import com.teamwizardry.librarianlib.foundation.registration.RegistrationManager
66
import dev.thecodewarrior.hooked.item.HookItem
77

88
object HookedModItems {
9+
val hooks = mutableListOf<LazyItem>()
910
val ironHook: LazyItem = LazyItem()
1011

1112
internal fun registerItems(registrationManager: RegistrationManager) {
12-
ironHook.from(registrationManager.add(
13-
ItemSpec("iron_hook")
14-
.maxStackSize(1)
15-
.item { HookItem(it.itemProperties, HookedModHookTypes.iron) }
16-
.datagen { simpleModel() }
17-
))
13+
HookedModHookTypes.types.forEach { type ->
14+
val item = registrationManager.add(
15+
ItemSpec(type.registryName!!.path)
16+
.maxStackSize(1)
17+
.item { HookItem(it.itemProperties, type) }
18+
.datagen { simpleModel() }
19+
)
20+
if(type.registryName!!.path == "iron_hook")
21+
ironHook.from(item)
22+
hooks.add(item)
23+
}
1824
registrationManager.itemGroupIcon = ironHook
1925
}
2026

2127
internal fun registerItemDatagen(registrationManager: RegistrationManager) {
22-
registrationManager.datagen.itemTags.add(HookedMod.HOOKED_CURIOS_TAG, ironHook.get())
28+
registrationManager.datagen.itemTags.add(HookedMod.HOOKED_CURIOS_TAG, *hooks.map { it.get() }.toTypedArray())
2329
}
2430
}

src/main/kotlin/dev/thecodewarrior/hooked/capability/HookedPlayerData.kt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@ class HookedPlayerData(val player: PlayerEntity): BaseCapability() {
3333

3434
var controller: HookPlayerController = HookPlayerController.NONE
3535

36-
var needsSync: Boolean = true
36+
/**
37+
* State that is only ever used on the *logical* server. This includes things like syncing status.
38+
*/
39+
class ServerState {
40+
val dirtyHooks: MutableList<Hook> = mutableListOf()
41+
var forceFullSyncToClient: Boolean = false
42+
var forceFullSyncToOthers: Boolean = false
43+
}
44+
var serverState: ServerState = ServerState()
3745

3846
override fun deserializeNBT(nbt: CompoundNBT) {
3947
val oldType = type
@@ -52,15 +60,6 @@ class HookedPlayerData(val player: PlayerEntity): BaseCapability() {
5260
return nbt
5361
}
5462

55-
/**
56-
* Marks this data to be synced in its entirety with clients.
57-
*
58-
* TODO: make single-hook sync packets for other players, since they aren't critical to keep exactly up to date.
59-
*/
60-
fun markForSync() {
61-
needsSync = true
62-
}
63-
6463
companion object {
6564
@JvmStatic
6665
@CapabilityInject(HookedPlayerData::class)

0 commit comments

Comments
 (0)