• 可根据所需跳着看!!!

1.开始分析

1. 使用工具

2.入口分析

  • 首先将项目克隆到本地,然后根据仓库所给的教程进行成反混淆的后jar包,记得git checkout到自己所需的版本

  • 根据jar包的签名定位到main入口,映入眼帘的是一堆参数,如图所示image.png
    image.png

- 往下寻找可以看到创建了2个进程用来执行渲染和游戏逻辑Game thread,跳进游戏的Runnable接口中image.png

- 进来后while死循环直到running为false。调用了startTick用来开始计时随后调用了render函数用于渲染,渲染结束后停止计数,直接跟进。发现tick没有具体的实现过程。

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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
 public void run() {  
this.thread = Thread.currentThread();
if (Runtime.getRuntime().availableProcessors() > 4) {
this.thread.setPriority(10);
}

try {
boolean bl = false;

while(this.running) {
if (this.crashReportSupplier != null) {
printCrashReport((CrashReport)this.crashReportSupplier.get());
return;
}

try {
TickDurationMonitor lv = TickDurationMonitor.create("Renderer");
boolean bl2 = this.shouldMonitorTickDuration();
this.profiler = this.startMonitor(bl2, lv);
this.profiler.startTick();
this.recorder.startTick();
this.render(!bl); // 跟进render
this.recorder.endTick();
this.profiler.endTick();
this.endMonitor(bl2, lv);
} catch (OutOfMemoryError outOfMemoryError) {
if (bl) {
throw outOfMemoryError;
}

this.cleanUpAfterCrash();
this.setScreen(new OutOfMemoryScreen());
System.gc();
LOGGER.error(LogUtils.FATAL_MARKER, "Out of memory", outOfMemoryError);
bl = true;
}
}
} catch (CrashException lv2) {
this.addDetailsToCrashReport(lv2.getReport());
this.cleanUpAfterCrash();
LOGGER.error(LogUtils.FATAL_MARKER, "Reported exception thrown!", lv2);
printCrashReport(lv2.getReport());
} catch (Throwable throwable) {
CrashReport lv3 = this.addDetailsToCrashReport(new CrashReport("Unexpected error", throwable));
LOGGER.error(LogUtils.FATAL_MARKER, "Unreported exception thrown!", throwable);
this.cleanUpAfterCrash();
printCrashReport(lv3);
}

}

//=================Render======================
private void render(boolean tick) {
this.window.setPhase("Pre render");
long l = Util.getMeasuringTimeNano();
if (this.window.shouldClose()) {
this.scheduleStop();
}

if (this.resourceReloadFuture != null && !(this.overlay instanceof SplashOverlay)) {
CompletableFuture<Void> completableFuture = this.resourceReloadFuture;
this.resourceReloadFuture = null;
this.reloadResources().thenRun(() -> completableFuture.complete((Object)null));
}

Runnable runnable;
while((runnable = (Runnable)this.renderTaskQueue.poll()) != null) {
runnable.run();
}

if (tick) {
int i = this.renderTickCounter.beginRenderTick(Util.getMeasuringTimeMs());
this.profiler.push("scheduledExecutables");
this.runTasks();
this.profiler.pop();
this.profiler.push("tick");

for(int j = 0; j < Math.min(10, i); ++j) {
this.profiler.visit("clientTick");
this.tick(); /// 跟进 tick1
}

this.profiler.pop();
}

this.mouse.updateMouse();
this.window.setPhase("Render");
this.profiler.push("sound");
this.soundManager.updateListenerPosition(this.gameRenderer.getCamera());
this.profiler.pop();
this.profiler.push("render");
long m = Util.getMeasuringTimeNano();
boolean bl2;
if (!this.options.debugEnabled && !this.recorder.isActive()) {
bl2 = false;
this.gpuUtilizationPercentage = (double)0.0F;
} else {
bl2 = this.currentGlTimerQuery == null || this.currentGlTimerQuery.isResultAvailable();
if (bl2) {
GlTimer.getInstance().ifPresent(GlTimer::beginProfile);
}
}

RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, IS_SYSTEM_MAC);
this.framebuffer.beginWrite(true);
BackgroundRenderer.clearFog();
this.profiler.push("display");
RenderSystem.enableCull();
this.profiler.pop();
if (!this.skipGameRender) {
this.profiler.swap("gameRenderer");
this.gameRenderer.render(this.paused ? this.pausedTickDelta : this.renderTickCounter.tickDelta, l, tick);
this.profiler.pop();
}

if (this.tickProfilerResult != null) {
this.profiler.push("fpsPie");
DrawContext lv = new DrawContext(this, this.bufferBuilders.getEntityVertexConsumers());
this.drawProfilerResults(lv, this.tickProfilerResult);
lv.draw();
this.profiler.pop();
}

this.profiler.push("blit");
this.framebuffer.endWrite();
this.framebuffer.draw(this.window.getFramebufferWidth(), this.window.getFramebufferHeight());
this.renderTime = Util.getMeasuringTimeNano() - m;
if (bl2) {
GlTimer.getInstance().ifPresent((glTimer) -> this.currentGlTimerQuery = glTimer.endProfile());
}

this.profiler.swap("updateDisplay");
this.window.swapBuffers();
int k = this.getFramerateLimit();
if (k < 260) {
RenderSystem.limitDisplayFPS(k);
}

this.profiler.swap("yield");
Thread.yield();
this.profiler.pop();
this.window.setPhase("Post render");
++this.fpsCounter;
boolean bl3 = this.isIntegratedServerRunning() && (this.currentScreen != null && this.currentScreen.shouldPause() || this.overlay != null && this.overlay.pausesGame()) && !this.server.isRemote();
if (this.paused != bl3) {
if (this.paused) {
this.pausedTickDelta = this.renderTickCounter.tickDelta;
} else {
this.renderTickCounter.tickDelta = this.pausedTickDelta;
}

this.paused = bl3;
}

long n = Util.getMeasuringTimeNano();
long o = n - this.lastMetricsSampleTime;
if (bl2) {
this.metricsSampleDuration = o;
}

this.metricsData.pushSample(o);
this.lastMetricsSampleTime = n;
this.profiler.push("fpsUpdate");
if (this.currentGlTimerQuery != null && this.currentGlTimerQuery.isResultAvailable()) {
this.gpuUtilizationPercentage = (double)this.currentGlTimerQuery.queryResult() * (double)100.0F / (double)this.metricsSampleDuration;
}

while(Util.getMeasuringTimeMs() >= this.nextDebugInfoUpdateTime + 1000L) {
String string;
if (this.gpuUtilizationPercentage > (double)0.0F) {
String var10000 = this.gpuUtilizationPercentage > (double)100.0F ? Formatting.RED + "100%" : Math.round(this.gpuUtilizationPercentage) + "%";
string = " GPU: " + var10000;
} else {
string = "";
}

currentFps = this.fpsCounter;
this.fpsDebugString = String.format(Locale.ROOT, "%d fps T: %s%s%s%s B: %d%s", currentFps, k == 260 ? "inf" : k, (Boolean)this.options.getEnableVsync().getValue() ? " vsync" : "", this.options.getGraphicsMode().getValue(), this.options.getCloudRenderMode().getValue() == CloudRenderMode.OFF ? "" : (this.options.getCloudRenderMode().getValue() == CloudRenderMode.FAST ? " fast-clouds" : " fancy-clouds"), this.options.getBiomeBlendRadius().getValue(), string);
this.nextDebugInfoUpdateTime += 1000L;
this.fpsCounter = 0;
}

this.profiler.pop();
}


//=================== tick 1 ====================
public void tick() {
if (this.itemUseCooldown > 0) {
--this.itemUseCooldown;
}

this.profiler.push("gui");
this.messageHandler.processDelayedMessages();
this.inGameHud.tick(this.paused);
this.profiler.pop();
this.gameRenderer.updateTargetedEntity(1.0F);
this.tutorialManager.tick(this.world, this.crosshairTarget);
this.profiler.push("gameMode");
if (!this.paused && this.world != null) {
this.interactionManager.tick();
}

this.profiler.swap("textures");
if (this.world != null) {
this.textureManager.tick();
}

if (this.currentScreen == null && this.player != null) {
if (this.player.isDead() && !(this.currentScreen instanceof DeathScreen)) {
this.setScreen((Screen)null);
} else if (this.player.isSleeping() && this.world != null) {
this.setScreen(new SleepingChatScreen());
}
} else {
Screen var2 = this.currentScreen;
if (var2 instanceof SleepingChatScreen) {
SleepingChatScreen lv = (SleepingChatScreen)var2;
if (!this.player.isSleeping()) {
lv.closeChatIfEmpty();
}
}
}

if (this.currentScreen != null) {
this.attackCooldown = 10000;
}

if (this.currentScreen != null) {
Screen.wrapScreenError(() -> this.currentScreen.tick(), "Ticking screen", this.currentScreen.getClass().getCanonicalName());
}

if (!this.options.debugEnabled) {
this.inGameHud.resetDebugHudChunk();
}

if (this.overlay == null && this.currentScreen == null) {
this.profiler.swap("Keybindings");
this.handleInputEvents();
if (this.attackCooldown > 0) {
--this.attackCooldown;
}
}

if (this.world != null) {
this.profiler.swap("gameRenderer");
if (!this.paused) {
this.gameRenderer.tick();
}

this.profiler.swap("levelRenderer");
if (!this.paused) {
this.worldRenderer.tick();
}

this.profiler.swap("level");
if (!this.paused) {
this.world.tickEntities();
}
} else if (this.gameRenderer.getPostProcessor() != null) {
this.gameRenderer.disablePostProcessor();
}

if (!this.paused) {
this.musicTracker.tick();
}

this.soundManager.tick(this.paused);
if (this.world != null) {
if (!this.paused) {
if (!this.options.joinedFirstServer && this.isConnectedToServer()) {
Text lv2 = Text.translatable("tutorial.socialInteractions.title");
Text lv3 = Text.translatable("tutorial.socialInteractions.description", new Object[]{TutorialManager.keyToText("socialInteractions")});
this.socialInteractionsToast = new TutorialToast(net.minecraft.client.toast.TutorialToast.Type.SOCIAL_INTERACTIONS, lv2, lv3, true);
this.tutorialManager.add(this.socialInteractionsToast, 160);
this.options.joinedFirstServer = true;
this.options.write();
}

this.tutorialManager.tick();

try {
this.world.tick(() -> true); // 跟进tick 2
} catch (Throwable throwable) {
CrashReport lv4 = CrashReport.create(throwable, "Exception in world tick");
if (this.world == null) {
CrashReportSection lv5 = lv4.addElement("Affected level");
lv5.add("Problem", "Level is null!");
} else {
this.world.addDetailsToCrashReport(lv4);
}

throw new CrashException(lv4);
}
}

this.profiler.swap("animateTick");
if (!this.paused && this.world != null) {
this.world.doRandomBlockDisplayTicks(this.player.getBlockX(), this.player.getBlockY(), this.player.getBlockZ());
}

this.profiler.swap("particles");
if (!this.paused) {
this.particleManager.tick();
}
} else if (this.integratedServerConnection != null) {
this.profiler.swap("pendingConnection");
this.integratedServerConnection.tick();
}

this.profiler.swap("keyboard");
this.keyboard.pollDebugCrash();
this.profiler.pop();
}


//================== tick 2 =====================
public void tick(BooleanSupplier shouldKeepTicking) {
this.getWorldBorder().tick();
this.tickTime();
if (this.lightningTicksLeft > 0) {
this.setLightningTicksLeft(this.lightningTicksLeft - 1);
}

this.getProfiler().push("blocks");
this.chunkManager.tick(shouldKeepTicking, true); // 跟进tick 3
this.getProfiler().pop();
}

//================== tick 3 ======================
public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) {
}

- 发现tick3中的函数没有具体的实现方法。
 - 通过查看类ClientChunkManger发现继承的ChunkManger,使用Jadx查看ChunkManager的交叉引用发现有个类ServerChunkManger也继承了该类。跳进去查看tick函数的实现,跟进tickChunk后发现该函数就是用于怪物的生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 public void tick(BooleanSupplier shouldKeepTicking, boolean tickChunks) {  
this.world.getProfiler().push("purge");
this.ticketManager.purge();
this.tick();
this.world.getProfiler().swap("chunks");
if (tickChunks) {
this.tickChunks(); // 跟进
}

this.world.getProfiler().swap("unload");
this.threadedAnvilChunkStorage.tick(shouldKeepTicking);
this.world.getProfiler().pop();
this.initChunkCaches();
}

2. 机制分析

1. 生成周期

  • 生成周期——怪物每次生成所间隔的时间

2. 基础生成

- 基础生成——每个周期都会尝试3次的生成

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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791

private void tickChunks() {  

  long l = this.world.getTime();  

  long m = l - this.lastMobSpawningTime;  

  this.lastMobSpawningTime = l;  

  boolean bl = this.world.isDebugWorld();  

  if (bl) {  

    this.threadedAnvilChunkStorage.tickEntityMovement();  

  } else {  

    WorldProperties lv = this.world.getLevelProperties();  

    Profiler lv2 = this.world.getProfiler();  

    lv2.push("pollingChunks");  

    int i = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED);  

    boolean bl2 = lv.getTime() % 400L == 0L// 每400gt = 400 * 0.05 = 20s进行一次生成

    lv2.push("naturalSpawnCount");  

    int j = this.ticketManager.getTickedChunkCount();  

    SpawnHelper.Info lv3 = SpawnHelper.setupSpawn(j, this.world.iterateEntities(), this::ifChunkLoaded, new SpawnDensityCapper(this.threadedAnvilChunkStorage));  

    this.spawnInfo = lv3;  

    lv2.swap("filteringLoadedChunks"); // 获取所有已加载的区块

    List<ChunkWithHolder> list = Lists.newArrayListWithCapacity(j);  

    for(ChunkHolder lv4 : this.threadedAnvilChunkStorage.entryIterator()) {  

      WorldChunk lv5 = lv4.getWorldChunk();  

      if (lv5 != null) {  

        list.add(new ChunkWithHolder(lv5, lv4));  

      }  

    }  

    lv2.swap("spawnAndTick"); // 开始生成工作

    boolean bl3 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING);  

    Collections.shuffle(list);  

    for(ChunkWithHolder lv6 : list) {  

      WorldChunk lv7 = lv6.chunk;  

      ChunkPos lv8 = lv7.getPos();  

      if (this.world.shouldTick(lv8) && this.threadedAnvilChunkStorage.shouldTick(lv8)) {  

        lv7.increaseInhabitedTime(m);  

        if (bl3 && (this.spawnMonsters || this.spawnAnimals) && this.world.getWorldBorder().contains(lv8)) { // 检查世界规则是否允许生成

          SpawnHelper.spawn(this.world, lv7, lv3, this.spawnAnimals, this.spawnMonsters, bl2); // 调用生成辅助函数, 跟进函数

        }  

        if (this.world.shouldTickBlocksInChunk(lv8.toLong())) {  

          this.world.tickChunk(lv7, i);  

        }  

      }  

    }  

    lv2.swap("customSpawners");  

    if (bl3) {  

      this.world.tickSpawners(this.spawnMonsters, this.spawnAnimals);  

    }  

    lv2.swap("broadcast");  

    list.forEach((chunk) -> chunk.holder.flushUpdates(chunk.chunk));  

    lv2.pop();  

    lv2.pop();  

    this.threadedAnvilChunkStorage.tickEntityMovement();  

  }  

}

// ==================================spawn==========================

public static void spawn(ServerWorld world, WorldChunk chunk, Info info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) {  

  world.getProfiler().push("spawner");  

  for(SpawnGroup lv : SPAWNABLE_GROUPS) {  

    if ((spawnAnimals || !lv.isPeaceful()) && (spawnMonsters || lv.isPeaceful()) && (rareSpawn || !lv.isRare()) && info.isBelowCap(lv, chunk.getPos())) {  

      Objects.requireNonNull(info);  

      Checker var10003 = info::test;  

      Objects.requireNonNull(info);  

      spawnEntitiesInChunk(lv, world, chunk, var10003, info::run); // 调用函数生成实体在区块中,继续跟进spawnEntitiesInChunk 1

    }  

  }  

  world.getProfiler().pop();  

}  

// ===========================getRandomPosInChunkSection=================

private static BlockPos getRandomPosInChunkSection(World world, WorldChunk chunk) {  

  ChunkPos lv = chunk.getPos();  

  int i = lv.getStartX() + world.random.nextInt(16);  

  int j = lv.getStartZ() + world.random.nextInt(16);  

  int k = chunk.sampleHeightmap(Type.WORLD_SURFACE, i, j) + 1;  

  int l = MathHelper.nextBetween(world.random, world.getBottomY(), k);  

  return new BlockPos(i, l, j);  

}

// ============================spawnEntitiesInChunk 1========================

public static void spawnEntitiesInChunk(SpawnGroup group, ServerWorld world, WorldChunk chunk, Checker checker, Runner runner) {  

  BlockPos lv = getRandomPosInChunkSection(world, chunk); // 从已加载的区块中获取随机的坐标,生物会刷新在该坐标中。X/Z坐标通过该区块的未知加上一个随机数生成。Y坐标则是通过获取当前的X/Z坐标中从世界Y坐标最小值-64到当前X/Z坐标中,最高实体方块的Y坐标+1。这里也是我们所称的LC值,LC值越小(Y轴越小)生物生成的概率会越大,其概率是1/LC因为随机数的范围是-64到最高实体方块的Y轴,如果Y越小随机到的概率会越大,例如:Y是50和Y是100的概率分别是1/(50 + 1 -(-64))和1/(100 + 1-(-64))

  if (lv.getY() >= world.getBottomY() + 1) { //如果坐标大于等于-64 + 1才会进行生成

    spawnEntitiesInChunk(group, world, chunk, lv, checker, runner); // 跟进spawnEntitiesInChunk 2

  }  

}  

// ========================spawnEntitiesInChunk 2============================

@Debug  

public static void spawnEntitiesInChunk(SpawnGroup group, ServerWorld world, BlockPos pos) {  

  spawnEntitiesInChunk(group, world, world.getChunk(pos), pos, (type, posx, chunk) -> true, (entity, chunk) -> {  

  }); // 跟进spawnEntitiesInChunk 3

}  

// ===========================spawnEntitiesInChunk 3=========================

public static void spawnEntitiesInChunk(SpawnGroup group, ServerWorld world, Chunk chunk, BlockPos pos, Checker checker, Runner runner) {  

  StructureAccessor lv = world.getStructureAccessor();  

  ChunkGenerator lv2 = world.getChunkManager().getChunkGenerator();  

  int i = pos.getY(); // 获取刚刚随机的Y坐标

  BlockState lv3 = chunk.getBlockState(pos);  

  if (!lv3.isSolidBlock(chunk, pos)) { // 判断是否是实体方块,如果不是则不进行生成

    BlockPos.Mutable lv4 = new BlockPos.Mutable();  

    int j = 0;  

    for(int k = 0; k < 3; ++k) { // 每次生成会进行3次的基础生成

      int l = pos.getX(); // 获取刚刚随机的X/Z坐标

      int m = pos.getZ();  

      int n = 6;  

      SpawnSettings.SpawnEntry lv5 = null;  

      EntityData lv6 = null;  

      int o = MathHelper.ceil(world.random.nextFloat() * 4.0F); // 随机一个数用于每次基础生成的生物实体数量 

      int p = 0;  

      for(int q = 0; q < o; ++q) { //怪物游走

        l += world.random.nextInt(6) - world.random.nextInt(6); // X/Z坐标每次随机走-5 至 5 格

        m += world.random.nextInt(6) - world.random.nextInt(6);  

        lv4.set(l, i, m);  

        double d = (double)l + (double)0.5F;  

        double e = (double)m + (double)0.5F;  

        PlayerEntity lv7 = world.getClosestPlayer(d, (double)i, e, (double)-1.0F, false); //获取距离该需要生成的坐标最近的玩家

        if (lv7 != null) { //如果附近没有最近的玩家则不进行生成

          double f = lv7.squaredDistanceTo(d, (double)i, e); // 获取玩最近玩家到该所需生成生物的X/Y/Z坐标的直线距离(欧拉距离)

          if (isAcceptableSpawnPosition(world, chunk, lv4, f)) // 判断当前随机的坐标是否满足生成条件, 详情见·isAcceptableSpawnPosition·函数

           {  

            if (lv5 == null) { // 第一次循环时lv5为null值,在这里对lv5进行赋值

              Optional<SpawnSettings.SpawnEntry> optional = pickRandomSpawnEntry(world, lv, lv2, group, world.random, lv4); // 获取随机的生成条目

              if (optional.isEmpty()) { // 如果为null则终止这一次的基础生成

                break;  

              }  

              lv5 = (SpawnSettings.SpawnEntry)optional.get();  

              o = lv5.minGroupSize + world.random.nextInt(1 + lv5.maxGroupSize - lv5.minGroupSize); // 实际每次基础生成需要生成的数量

            }  

            if (canSpawn(world, group, lv, lv2, lv5, lv4, f) && checker.test(lv5.type, lv4, chunk)) { // 判断是否符合条件,若符合则进行生成 跟进 canSpawn 1

              MobEntity lv8 = createMob(world, lv5.type); // 根据随机的条目获取其生物类型创建生物实体

              if (lv8 == null) { // 若创建失败则终止本次周期的所有基础生成

                return;  

              }  

              lv8.refreshPositionAndAngles(d, (double)i, e, world.random.nextFloat() * 360.0F, 0.0F); // 对刚刚创建的实体设置其坐标

              if (isValidSpawn(world, lv8, f)) { // 判断是否如何生成 跟进 isValidSpawn

                lv6 = lv8.initialize(world, world.getLocalDifficulty(lv8.getBlockPos()), SpawnReason.NATURAL, lv6, (NbtCompound)null); // 根据世界难度对生物进行初始化数值

                ++j;  

                ++p;  

                world.spawnEntityAndPassengers(lv8); // 生成实体

                runner.run(lv8, chunk);  

                if (j >= lv8.getLimitPerChunk()) {  

                  return// 如果超过每个区块的最大值则终止本次周期的所有基础生成

                }  

                if (lv8.spawnsTooManyForEachTry(p)) {  

                  break;  

                }  

              }  

            }  

          }  

        }  

      }  

    }  

  }  

}

// ========================isAcceptableSpawnPosition===================

private static boolean isAcceptableSpawnPosition(ServerWorld world, Chunk chunk, BlockPos.Mutable pos, double squaredDistance) {  

  if (squaredDistance <= (double)576.0F) { // 判断距离是否大于等于 24 * 24, 这里的squaredDistance是距离玩家的直线距离并且没有进行开平方运算,所以需要 24 * 24, 如过距离小于<=24则不进行刷怪

    return false;  

  } else if (world.getSpawnPos().isWithinDistance(new Vec3d((double)pos.getX() + (double)0.5F, (double)pos.getY(), (double)pos.getZ() + (double)0.5F), (double)24.0F)) { // 获取出生点的位置,同上判断 

    return false;  

  } else {  

    return Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.shouldTick(pos); // 判断当前的位置是否相等

  }  

}

// ==========================canSpawn 1====================================

private static boolean canSpawn(ServerWorld world, SpawnGroup group, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, SpawnSettings.SpawnEntry spawnEntry, BlockPos.Mutable pos, double squaredDistance) {  

  EntityType<?> lv = spawnEntry.type;  

  if (lv.getSpawnGroup() == SpawnGroup.MISC) { // 若生成类型是 杂项则不生成

    return false;  

  } else if (!lv.isSpawnableFarFromPlayer() && squaredDistance > (double)(lv.getSpawnGroup().getImmediateDespawnRange() * lv.getSpawnGroup().getImmediateDespawnRange())) { // 若坐标离玩家很近,并且直线距离超过了128则不进行生成, 详情看SpawnGroup

    return false;  

  } else if (lv.isSummonable() && containsSpawnEntry(world, structureAccessor, chunkGenerator, group, spawnEntry, pos)) { // 判断所生成的生物是否可召唤,并且包含在条目里面

    SpawnRestriction.Location lv2 = SpawnRestriction.getLocation(lv); // 获取该坐标的位置限制区域。

    if (!canSpawn(lv2, world, pos, lv)) // 判断是否可生成, 跟进 canSpawn 2{  

      return false;  

    } else if (!SpawnRestriction.canSpawn(lv, world, SpawnReason.NATURAL, pos, world.random)) {  // 判断当前区域是否可以生成对应的生物,比如史莱姆需要在史莱姆区块或者沼泽,每个生物实体都有自己的canSpawn检测函数, 详情看SpawnRestriction

      return false;  

    } else {  

      return world.isSpaceEmpty(lv.createSimpleBoundingBox((double)pos.getX() + (double)0.5F, (double)pos.getY(), (double)pos.getZ() + (double)0.5F));  // 判断当前生物的坐标是否是有其他的东西在上面,若没有则进行生成

    }  

  } else {  

    return false// 不生成

  }  

}

// ==============================isValidSpawn==================================

private static boolean isValidSpawn(ServerWorld world, MobEntity entity, double squaredDistance) {  

  if (squaredDistance > (double)(entity.getType().getSpawnGroup().getImmediateDespawnRange() * entity.getType().getSpawnGroup().getImmediateDespawnRange()) && entity.canImmediatelyDespawn(squaredDistance)) { // 判断坐标与玩家之间的距离是否 > 128, 若是则不生成

    return false;  

  } else {  

    return entity.canSpawn(world, SpawnReason.NATURAL) && entity.canSpawn(world); // 

  }  

}

// ============================ canSpawn 2 =====================================

public static boolean canSpawn(SpawnRestriction.Location location, WorldView world, BlockPos pos, @Nullable EntityType<?> entityType) {  

  if (location == Location.NO_RESTRICTIONS) {  

    return true// 如果区域是不限制的则进行生成 

  } else if (entityType != null && world.getWorldBorder().contains(pos)) { // 若生成的实体不为null且世界边界里包含了该坐标则 

    BlockState lv = world.getBlockState(pos); // 获取方块的状态

    FluidState lv2 = world.getFluidState(pos); // 获取流体状态

    BlockPos lv3 = pos.up(); // 获取方块上面/下面

    BlockPos lv4 = pos.down();  

    switch (location) {  

      case IN_WATER: // 如果生成区域在水里

        return lv2.isIn(FluidTags.WATER) && !world.getBlockState(lv3).isSolidBlock(world, lv3);  

      case IN_LAVA: // 在岩浆里

        return lv2.isIn(FluidTags.LAVA);  

      case ON_GROUND: // 在地表面 或 其他

      default:  

        BlockState lv5 = world.getBlockState(lv4);  

        if (!lv5.allowsSpawning(world, lv4, entityType)) { // 当前方块是否是实体方块, 不是则不生成 this.allowsSpawningPredicate = (state, world, pos, type) -> state.isSideSolidFullSquare(world, pos, Direction.UP) && state.getLuminance() < 14;

          return false;  

        } else// 当前坐标上面是否没有东西, 是就生成

          return isClearForSpawn(world, pos, lv, lv2, entityType) && isClearForSpawn(world, lv3, world.getBlockState(lv3), world.getFluidState(lv3), entityType);  

        }  

    }  

  } else {  

    return false;  

  }  

}

// ============================SpawnGroup====================================

public enum SpawnGroup implements StringIdentifiable {  

  MONSTER("monster", 70, false, false, 128),  

  CREATURE("creature", 10, true, true, 128),  

  AMBIENT("ambient", 15, true, false, 128),  

  AXOLOTLS("axolotls", 5, true, false, 128),  

  UNDERGROUND_WATER_CREATURE("underground_water_creature", 5, true, false, 128),  

  WATER_CREATURE("water_creature", 5, true, false, 128),  

  WATER_AMBIENT("water_ambient", 20, true, false, 64),  

  MISC("misc", -1, true, true, 128);  

  public static final Codec<SpawnGroup> CODEC = StringIdentifiable.createCodec(SpawnGroup::values);  

  private final int capacity;  

  private final boolean peaceful;  

  private final boolean rare;  

  private final String name;  

  private final int despawnStartRange = 32;  

  private final int immediateDespawnRange;  

  private SpawnGroup(String name, int spawnCap, boolean peaceful, boolean rare, int immediateDespawnRange) {  

    this.name = name;

    this.capacity = spawnCap; // 生成最大存在数量

    this.peaceful = peaceful; // 是否在和平模式可生成

    this.rare = rare; // 是否是稀有的

    this.immediateDespawnRange = immediateDespawnRange; // 立刻清除生成的范围

  }  

  public String getName() {  

    return this.name;  

  }  

  public String asString() {  

    return this.name;  

  }  

  public int getCapacity() {  

    return this.capacity;  

  }  

  public boolean isPeaceful() {  

    return this.peaceful;  

  }  

  public boolean isRare() {  

    return this.rare;  

  }  

  public int getImmediateDespawnRange() {  

    return this.immediateDespawnRange;  

  }  

  public int getDespawnStartRange() {  

    return 32;  

  }  

}

// ========================== SpawnRestriction ================================

public class SpawnRestriction {  

  private static final Map<EntityType<?>, Entry> RESTRICTIONS = Maps.newHashMap();  

  private static <T extends MobEntity> void register(EntityType<T> type, Location location, Heightmap.Type heightmapType, SpawnPredicate<T> predicate) {  

    Entry lv = (Entry)RESTRICTIONS.put(type, new Entry(heightmapType, location, predicate));  

    if (lv != null) {  

      throw new IllegalStateException("Duplicate registration for type " + Registries.ENTITY_TYPE.getId(type));  

    }  

  }  

  public static Location getLocation(EntityType<?> type) {  

    Entry lv = (Entry)RESTRICTIONS.get(type);  

    return lv == null ? SpawnRestriction.Location.NO_RESTRICTIONS : lv.location;  

  }  

  public static Heightmap.Type getHeightmapType(@Nullable EntityType<?> type) {  

    Entry lv = (Entry)RESTRICTIONS.get(type);  

    return lv == null ? Type.MOTION_BLOCKING_NO_LEAVES : lv.heightmapType;  

  }  

  public static <T extends Entity> boolean canSpawn(EntityType<T> type, ServerWorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) { // 根据不同的生物获取其类型使用对应的生成检测器 

    Entry lv = (Entry)RESTRICTIONS.get(type);  

    return lv == null || lv.predicate.test(type, world, spawnReason, pos, random);  

  }  

  static// 注册每个生物自己的生成检测器

    register(EntityType.AXOLOTL, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, AxolotlEntity::canSpawn);  

    register(EntityType.COD, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, WaterCreatureEntity::canSpawn);  

    register(EntityType.DOLPHIN, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, WaterCreatureEntity::canSpawn);  

    register(EntityType.DROWNED, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, DrownedEntity::canSpawn);  

    register(EntityType.GUARDIAN, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, GuardianEntity::canSpawn);  

    register(EntityType.PUFFERFISH, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, WaterCreatureEntity::canSpawn);  

    register(EntityType.SALMON, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, WaterCreatureEntity::canSpawn);  

    register(EntityType.SQUID, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, WaterCreatureEntity::canSpawn);  

    register(EntityType.TROPICAL_FISH, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, TropicalFishEntity::canTropicalFishSpawn);  

    register(EntityType.BAT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, BatEntity::canSpawn);  

    register(EntityType.BLAZE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnIgnoreLightLevel);  

    register(EntityType.CAVE_SPIDER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.CHICKEN, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.COW, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.CREEPER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.DONKEY, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.ENDERMAN, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.ENDERMITE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, EndermiteEntity::canSpawn);  

    register(EntityType.ENDER_DRAGON, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.FROG, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, FrogEntity::canSpawn);  

    register(EntityType.GHAST, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, GhastEntity::canSpawn);  

    register(EntityType.GIANT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.GLOW_SQUID, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, GlowSquidEntity::canSpawn);  

    register(EntityType.GOAT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, GoatEntity::canSpawn);  

    register(EntityType.HORSE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.HUSK, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HuskEntity::canSpawn);  

    register(EntityType.IRON_GOLEM, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.LLAMA, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.MAGMA_CUBE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MagmaCubeEntity::canMagmaCubeSpawn);  

    register(EntityType.MOOSHROOM, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MooshroomEntity::canSpawn);  

    register(EntityType.MULE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.OCELOT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING, OcelotEntity::canSpawn);  

    register(EntityType.PARROT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING, ParrotEntity::canSpawn);  

    register(EntityType.PIG, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.HOGLIN, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HoglinEntity::canSpawn);  

    register(EntityType.PIGLIN, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, PiglinEntity::canSpawn);  

    register(EntityType.PILLAGER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, PatrolEntity::canSpawn);  

    register(EntityType.POLAR_BEAR, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, PolarBearEntity::canSpawn);  

    register(EntityType.RABBIT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, RabbitEntity::canSpawn);  

    register(EntityType.SHEEP, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.SILVERFISH, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, SilverfishEntity::canSpawn);  

    register(EntityType.SKELETON, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.SKELETON_HORSE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.SLIME, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, SlimeEntity::canSpawn);  

    register(EntityType.SNOW_GOLEM, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.SPIDER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.STRAY, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, StrayEntity::canSpawn);  

    register(EntityType.STRIDER, SpawnRestriction.Location.IN_LAVA, Type.MOTION_BLOCKING_NO_LEAVES, StriderEntity::canSpawn);  

    register(EntityType.TURTLE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, TurtleEntity::canSpawn);  

    register(EntityType.VILLAGER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.WITCH, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.WITHER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.WITHER_SKELETON, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.WOLF, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, WolfEntity::canSpawn);  

    register(EntityType.ZOMBIE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.ZOMBIE_HORSE, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.ZOMBIFIED_PIGLIN, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, ZombifiedPiglinEntity::canSpawn);  

    register(EntityType.ZOMBIE_VILLAGER, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.CAT, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.ELDER_GUARDIAN, SpawnRestriction.Location.IN_WATER, Type.MOTION_BLOCKING_NO_LEAVES, GuardianEntity::canSpawn);  

    register(EntityType.EVOKER, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.FOX, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, FoxEntity::canSpawn);  

    register(EntityType.ILLUSIONER, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.PANDA, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.PHANTOM, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.RAVAGER, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.SHULKER, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.TRADER_LLAMA, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, AnimalEntity::isValidNaturalSpawn);  

    register(EntityType.VEX, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.VINDICATOR, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnInDark);  

    register(EntityType.WANDERING_TRADER, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

    register(EntityType.WARDEN, SpawnRestriction.Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, MobEntity::canMobSpawn);  

  }  

  static class Entry {  

    final Heightmap.Type heightmapType;  

    final Location location;  

    final SpawnPredicate<?> predicate;  

    public Entry(Heightmap.Type heightmapType, Location location, SpawnPredicate<?> predicate) {  

      this.heightmapType = heightmapType;  

      this.location = location;  

      this.predicate = predicate;  

    }  

  }  

  public static enum Location {  

    ON_GROUND,  

    IN_WATER,  

    NO_RESTRICTIONS,  

    IN_LAVA;  

  }  

  @FunctionalInterface  

  public interface SpawnPredicate<T extends Entity> {  

    boolean test(EntityType<T> type, ServerWorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random);  

  }  

}

// ========================== 史莱姆生成器 ===============================

register(EntityType.SLIME, SpawnRestriction.Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, SlimeEntity::canSpawn); // 直接跟进史莱姆的canSpawn

public static boolean canSpawn(EntityType<SlimeEntity> type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) {  

  if (world.getDifficulty() != Difficulty.PEACEFUL) {  

    if (world.getBiome(pos).isIn(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70 && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonSize() && world.getLightLevel(pos) <= random.nextInt(8)) { // 判断坐标是否位于可生成史莱姆的表面, 且Y坐标>50<70的位置,生成一个随机数跟当前坐标的亮度进行比较,如果当前亮度小于这个随机数则进行生成。这里也就是史莱姆的生成概率为:1/(7 - 当前亮度),可得出若亮度越低史莱姆生成的概率越高

      return canMobSpawn(type, world, spawnReason, pos, random);

    }  

    if (!(world instanceof StructureWorldAccess)) {  

      return false;  

    }  

    ChunkPos lv = new ChunkPos(pos);  

    boolean bl = ChunkRandom.getSlimeRandom(lv.x, lv.z, ((StructureWorldAccess)world).getSeed(), 987234911L).nextInt(10) == 0;  

    if (random.nextInt(10) == 0 && bl && pos.getY() < 40) {  

    // 若不在可生成史莱姆则根据世界种子随机一个区块当作史莱姆区块, 判断是否在史莱姆区块中, 且随机一个 0 - 9 的随机数如果是0,且Y轴小于40则生成史莱姆。所以史莱姆区块中生成史莱姆的概率是1 / 10

      return canMobSpawn(type, world, spawnReason, pos, random);  

    }  

  }  

  return false;  

}

3. 总结

  • 每次生成的周期是20s

  • 每次生成检测范围:以玩家为圆心的球形区域,若坐标小于或等于半径24则不生成,若坐标大于半径24且小于128则生成

  • 当前X/Z坐标的最高实体方块的Y坐标+1为max,则生成的概率是1/(max - (-64)), 当Y越小概率越大,这既是LC值

  • 每个生物有自己的生成检测器用于判断该生物在该群系是否能生成