summaryrefslogtreecommitdiff
path: root/server/block_def.go
blob: 28ae0ef9984424b44c675106ed80f6b810cc7e29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package server

import (
	"iter"
	"git.citrons.xyz/metronode/classic"
)

type blockType int16

const (
	walkThrough = classic.WalkThrough
	swimThrough = classic.SwimThrough
	solid = classic.Solid
	partiallySlippery = classic.PartiallySlippery
	fullySlippery = classic.FullySlippery
	water = classic.Water
	lava = classic.Lava
	rope = classic.Rope
)
type blockSolidity byte

const (
	noSound = classic.NoSound
	woodSound = classic.WoodSound
	gravelSound = classic.GravelSound
	grassSound = classic.GrassSound
	stoneSound = classic.StoneSound
	metalSound = classic.MetalSound
	glassSound = classic.GlassSound
	woolSound = classic.WoolSound
	sandSound = classic.SandSound
	snowSound = classic.SnowSound
)
type blockSound byte

const (
	opaque = classic.Opaque
	transparentGlass = classic.TransparentGlass
	transparentLeaves = classic.TransparentLeaves
	translucent = classic.Translucent
	invisible = classic.Invisible
)
type blockOpacity byte

const (
	cubeShape = iota
	spriteShape
	cuboidShape
)
type blockShape byte

type textureId int
const fillTextures = -1

type blockDef struct {
	Name string
	Solidity blockSolidity
	MovementSpeed byte
	Textures [6]textureId
	TransmitsLight bool
	WalkSound blockSound
	FullBright bool
	Shape blockShape
	Min [3]byte
	Max [3]byte
	Opacity blockOpacity
	FogDensity byte
	FogColor [3]byte
	AuthLevel authLevel
	Variants map[any]blockType
}

type rotateVariant int
type slabMaterial struct{}
type slabVariant bool

func markVariants(variants map[any]blockType) {
	for _, block := range variants {
		def := blockDefinitions[block]
		if def.Variants == nil {
			def.Variants = make(map[any]blockType)
			blockDefinitions[block] = def
		}
		for variation, block := range variants {
			def.Variants[variation] = block
		}
	}
}

func assertNoDef(block blockType) {
	if _, ok := blockDefinitions[block]; ok {
		panic("variant would override existing block")
	}
}

var rotationNames = []string {"-N", "-E", "-S", "-W"}
func makeRotations(block blockType) {
	oldDef := blockDefinitions[block]
	variants := make(map[any]blockType)
	for i := 0; i < 4; i++ {
		newBlock := block + blockType(i)
		if i != 0 {
			assertNoDef(newBlock)
		}
		newDef := oldDef
		for j := 0; j < 4; j++ {
			k := (j + i) % 4
			newDef.Textures[j + 1] = oldDef.Textures[k + 1]
		}
		var minX, minZ, maxX, maxZ byte
		switch i {
		case 1:
			minX = oldDef.Min[2]
			minZ = 16 - oldDef.Min[0]
			maxX = oldDef.Max[2]
			maxZ = 16 - oldDef.Max[0]
		case 2:
			minX = 16 - oldDef.Min[0]
			minZ = 16 - oldDef.Min[2]
			maxX = 16 - oldDef.Max[0]
			maxZ = 16 - oldDef.Max[2]
		case 3:
			minX = oldDef.Min[2]
			minZ = 16 - oldDef.Min[0]
			maxX = oldDef.Max[2]
			maxZ = 16 - oldDef.Max[0]
		}
		newDef.Min = [3]byte {byte(minX), oldDef.Min[1], byte(minZ)}
		newDef.Max = [3]byte {byte(maxX), oldDef.Max[1], byte(maxZ)}
		newDef.Name = newDef.Name + rotationNames[i]
		variants[rotateVariant(i)] = newBlock
		blockDefinitions[newBlock] = newDef
	}
	markVariants(variants)
}

func makeSlabs(material blockType, slab blockType, name string) {
	materialDef := blockDefinitions[material]
	variants := make(map[any]blockType)
	variants[slabMaterial{}] = material

	if name == "" {
		name = materialDef.Name + " Slab"
	}

	bottomSlab := materialDef
	bottomSlab.Name = name + "-D"
	bottomSlab.Shape = cuboidShape
	bottomSlab.Min = [3]byte {0, 0, 0}
	bottomSlab.Max = [3]byte {16, 8, 16}
	switch material {
	default:
		assertNoDef(slab)
		variants[slabVariant(false)] = slab
		blockDefinitions[slab] = bottomSlab
	case blockDoubleSlab:
		variants[slabVariant(false)] = blockSlab
		blockDefinitions[blockSlab] = bottomSlab
		slab--
	case blockCobblestone:
		variants[slabVariant(false)] = blockCobblestoneSlab
		blockDefinitions[blockCobblestoneSlab] = bottomSlab
		slab--
	}
	topSlab := materialDef
	topSlab.Name = name + "-U"
	topSlab.Shape = cuboidShape
	topSlab.Min = [3]byte {0, 8, 0}
	topSlab.Max = [3]byte {16, 16, 16}
	assertNoDef(slab + 1)
	variants[slabVariant(true)] = slab + 1
	blockDefinitions[slab + 1] = topSlab

	markVariants(variants)
}

func getBlockDefPackets() iter.Seq[classic.Packet] {
	return func(yield func(classic.Packet) bool) {
		for id, def := range blockDefinitions { 
			var packet classic.Packet
			var (
				transmitsLight byte
				fullBright byte
			)
			if def.TransmitsLight {
				transmitsLight = 1
			}
			if def.FullBright {
				fullBright = 1
			}
			if def.Shape != spriteShape {
				var (i int; texture textureId)
				for i, texture = range def.Textures {
					if texture == fillTextures {
						break
					}
				}
				if texture == fillTextures {
					for i = i; i < len(def.Textures); i++ {
						def.Textures[i] = def.Textures[i - 1]
					}
				}
				if def.Shape == cubeShape {
					def.Min = [3]byte {0, 0, 0}
					def.Max = [3]byte {16, 16, 16}
				}
				packet = &classic.DefineBlockExt {
					BlockId: byte(id),
					Name: classic.PadString(def.Name),
					Solidity: byte(def.Solidity),
					MovementSpeed: def.MovementSpeed,
					TopTextureId: byte(def.Textures[0]),
					LeftTextureId: byte(def.Textures[4]),
					RightTextureId: byte(def.Textures[2]),
					FrontTextureId: byte(def.Textures[3]),
					BackTextureId: byte(def.Textures[1]),
					BottomTextureId: byte(def.Textures[5]),
					TransmitsLight: transmitsLight,
					WalkSound: byte(def.WalkSound),
					FullBright: fullBright,
					Min: def.Min,
					Max: def.Max,
					BlockDraw: byte(def.Opacity),
					FogDensity: def.FogDensity,
					FogColor: def.FogColor,
				}
			} else {
				packet = &classic.DefineBlock {
					BlockId: byte(id),
					Name: classic.PadString(def.Name),
					Solidity: byte(def.Solidity),
					MovementSpeed: def.MovementSpeed,
					SideTextureId: byte(def.Textures[1]),
					TransmitsLight: transmitsLight,
					WalkSound: byte(def.WalkSound),
					FullBright: fullBright,
					Shape: 0,
					BlockDraw: byte(def.Opacity),
					FogDensity: def.FogDensity,
					FogColor: def.FogColor,
				}
			}
			if !yield(packet) {
				return
			}
		}
	}
}

func getInventoryPackets(inventoryList []blockType) iter.Seq[classic.Packet] {
	return func(yield func(classic.Packet) bool) {
		var ok bool
		for i := 0; i < 256; i++ { // clear inventory first
			ok = yield(&classic.InventoryOrder {
				Order: 0,
				BlockId: byte(i),
			})
		}
		for i, block := range inventoryList {
			ok = yield(&classic.InventoryOrder {
				Order: byte(i + 1),
				BlockId: byte(block),
			})
		}
		if !ok {
			return
		}
	}
}

func getBlockPermissionPackets(auth authLevel) iter.Seq[classic.Packet] {
	return func(yield func(classic.Packet) bool) {
		for block, def := range blockDefinitions {
			var packet classic.Packet
			if auth < def.AuthLevel {
				packet = &classic.SetBlockPermission {
					BlockId: byte(block),
					AllowPlacement: 0,
					AllowDeletion: 0,
				}
			} else {
				packet = &classic.SetBlockPermission {
					BlockId: byte(block),
					AllowPlacement: 1,
					AllowDeletion: 1,
				}
			}
			if !yield(packet) {
				return
			}
		}
		var placeLiquids byte
		if auth >= defaultAuth {
			placeLiquids = 1
		}
		var ok bool
		ok = yield(&classic.SetBlockPermission {
			BlockId: blockWater,
			AllowPlacement: placeLiquids,
			AllowDeletion: placeLiquids,
		})
		if !ok {
			return
		}
		yield(&classic.SetBlockPermission {
			BlockId: blockLava,
			AllowPlacement: placeLiquids,
			AllowDeletion: placeLiquids,
		})
	}
}