-
Notifications
You must be signed in to change notification settings - Fork 630
Expand file tree
/
Copy pathSlimefun.java
More file actions
1100 lines (965 loc) · 43.3 KB
/
Slimefun.java
File metadata and controls
1100 lines (965 loc) · 43.3 KB
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
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package io.github.thebusybiscuit.slimefun4.implementation;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BundlingListener;
import io.github.thebusybiscuit.slimefun4.storage.Storage;
import io.github.thebusybiscuit.slimefun4.storage.backend.legacy.LegacyStorage;
import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.Recipe;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
import io.github.bakedlibs.dough.config.Config;
import io.github.bakedlibs.dough.protection.ProtectionManager;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.api.exceptions.TagMisconfigurationException;
import io.github.thebusybiscuit.slimefun4.api.geo.GEOResource;
import io.github.thebusybiscuit.slimefun4.api.gps.GPSNetwork;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.SlimefunRegistry;
import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import io.github.thebusybiscuit.slimefun4.core.services.AnalyticsService;
import io.github.thebusybiscuit.slimefun4.core.services.AutoSavingService;
import io.github.thebusybiscuit.slimefun4.core.services.BackupService;
import io.github.thebusybiscuit.slimefun4.core.services.BlockDataService;
import io.github.thebusybiscuit.slimefun4.core.services.CustomItemDataService;
import io.github.thebusybiscuit.slimefun4.core.services.CustomTextureService;
import io.github.thebusybiscuit.slimefun4.core.services.LocalizationService;
import io.github.thebusybiscuit.slimefun4.core.services.MetricsService;
import io.github.thebusybiscuit.slimefun4.core.services.MinecraftRecipeService;
import io.github.thebusybiscuit.slimefun4.core.services.PerWorldSettingsService;
import io.github.thebusybiscuit.slimefun4.core.services.PermissionsService;
import io.github.thebusybiscuit.slimefun4.core.services.ThreadService;
import io.github.thebusybiscuit.slimefun4.core.services.UpdaterService;
import io.github.thebusybiscuit.slimefun4.core.services.github.GitHubService;
import io.github.thebusybiscuit.slimefun4.core.services.holograms.HologramsService;
import io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler;
import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundService;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.Cooler;
import io.github.thebusybiscuit.slimefun4.implementation.items.magical.BeeWings;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.GrapplingHook;
import io.github.thebusybiscuit.slimefun4.implementation.items.weapons.SeismicAxe;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.AncientAltarListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.AutoCrafterListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeWingsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CargoNodeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.DispenserListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.EnhancedFurnaceListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GadgetsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.HopperListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.JoinListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MiddleClickListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MiningAndroidListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.RadioactivityListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBootsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBowListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemConsumeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemHitListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemInteractListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.VillagerTradingListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.AnvilListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.BrewingStandListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CartographyTableListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CauldronListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.CraftingTableListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.GrindstoneListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.crafting.SmithingTableListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.BeeListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.EntityInteractionListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.FireworksListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.IronGolemListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.PiglinListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.entity.WitherListener;
import io.github.thebusybiscuit.slimefun4.implementation.resources.GEOResourcesSetup;
import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup;
import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
import io.github.thebusybiscuit.slimefun4.implementation.setup.SlimefunItemSetup;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.SlimefunStartupTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.RadiationTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.RainbowArmorTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.SlimefunArmorTask;
import io.github.thebusybiscuit.slimefun4.implementation.tasks.armor.SolarHelmetTask;
import io.github.thebusybiscuit.slimefun4.integrations.IntegrationsManager;
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
import io.papermc.lib.PaperLib;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.MenuListener;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
/**
* This is the main class of Slimefun.
* This is where all the magic starts, take a look around.
*
* @author TheBusyBiscuit
*/
public class Slimefun extends JavaPlugin implements SlimefunAddon {
/**
* This is the Java version we recommend server owners to use.
* This does not necessarily mean that it's the minimum version
* required to run Slimefun.
*/
private static final int RECOMMENDED_JAVA_VERSION = 17;
/**
* Our static instance of {@link Slimefun}.
* Make sure to clean this up in {@link #onDisable()}!
*/
private static Slimefun instance;
/**
* Keep track of which {@link MinecraftVersion} we are on.
*/
private MinecraftVersion minecraftVersion = MinecraftVersion.UNKNOWN;
/**
* Keep track of whether this is a fresh install or a regular boot up.
*/
private boolean isNewlyInstalled = false;
// Various things we need
private final SlimefunRegistry registry = new SlimefunRegistry();
private final SlimefunCommand command = new SlimefunCommand(this);
private final TickerTask ticker = new TickerTask();
// Services - Systems that fulfill certain tasks, treat them as a black box
private final CustomItemDataService itemDataService = new CustomItemDataService(this, "slimefun_item");
private final BlockDataService blockDataService = new BlockDataService(this, "slimefun_block");
private final CustomTextureService textureService = new CustomTextureService(new Config(this, "item-models.yml"));
private final GitHubService gitHubService = new GitHubService("Slimefun/Slimefun4");
private final UpdaterService updaterService = new UpdaterService(this, getDescription().getVersion(), getFile());
private final MetricsService metricsService = new MetricsService(this);
private final AutoSavingService autoSavingService = new AutoSavingService();
private final BackupService backupService = new BackupService();
private final PermissionsService permissionsService = new PermissionsService(this);
private final PerWorldSettingsService worldSettingsService = new PerWorldSettingsService(this);
private final MinecraftRecipeService recipeService = new MinecraftRecipeService(this);
private final HologramsService hologramsService = new HologramsService(this);
private final SoundService soundService = new SoundService(this);
private final ThreadService threadService = new ThreadService(this);
private final AnalyticsService analyticsService = new AnalyticsService(this);
// Some other things we need
private final IntegrationsManager integrations = new IntegrationsManager(this);
private final SlimefunProfiler profiler = new SlimefunProfiler();
private final GPSNetwork gpsNetwork = new GPSNetwork(this);
// Even more things we need
private NetworkManager networkManager;
private LocalizationService local;
// Important config files for Slimefun
private final Config config = new Config(this);
private final Config items = new Config(this, "Items.yml");
private final Config researches = new Config(this, "Researches.yml");
// Data storage
private Storage playerStorage;
// Listeners that need to be accessed elsewhere
private final GrapplingHookListener grapplingHookListener = new GrapplingHookListener();
private final BackpackListener backpackListener = new BackpackListener();
private final SlimefunBowListener bowListener = new SlimefunBowListener();
/**
* This constructor is invoked by Bukkit and within unit tests.
* Therefore we need to figure out if we're within unit tests or not.
*/
public Slimefun() {
super();
// Check that we got loaded by MockBukkit rather than Bukkit's loader
// TODO: This is very much a hack and we can hopefully move to a more native way in the future
if (getClassLoader().getClass().getPackageName().startsWith("be.seeseemelk.mockbukkit")) {
minecraftVersion = MinecraftVersion.UNIT_TEST;
}
}
/**
* This is called when the {@link Plugin} has been loaded and enabled on a {@link Server}.
*/
@Override
public void onEnable() {
setInstance(this);
if (isUnitTest()) {
// We handle Unit Tests seperately.
onUnitTestStart();
} else if (isVersionUnsupported()) {
// We wanna ensure that the Server uses a compatible version of Minecraft.
getServer().getPluginManager().disablePlugin(this);
} else {
// The Environment has been validated.
onPluginStart();
}
}
/**
* This is our start method for a Unit Test environment.
*/
private void onUnitTestStart() {
local = new LocalizationService(this, "", null);
networkManager = new NetworkManager(200);
command.register();
registry.load(this, config);
loadTags();
soundService.reload(false);
// TODO: What do we do if tests want to use another storage backend (e.g. testing new feature on legacy + sql)?
// Do we have a way to override this?
playerStorage = new LegacyStorage();
}
/**
* This is our start method for a correct Slimefun installation.
*/
private void onPluginStart() {
long timestamp = System.nanoTime();
Logger logger = getLogger();
// Check if Paper (<3) is installed
if (PaperLib.isPaper()) {
logger.log(Level.INFO, "Paper was detected! Performance optimizations have been applied.");
} else {
PaperLib.suggestPaper(this);
}
// Check if CS-CoreLib is installed (it is no longer needed)
if (getServer().getPluginManager().getPlugin("CS-CoreLib") != null) {
StartupWarnings.discourageCSCoreLib(logger);
getServer().getPluginManager().disablePlugin(this);
return;
}
// Encourage newer Java version
if (NumberUtils.getJavaVersion() < RECOMMENDED_JAVA_VERSION) {
StartupWarnings.oldJavaVersion(logger, RECOMMENDED_JAVA_VERSION);
}
// If the server has no "data-storage" folder, it's _probably_ a new install. So mark it for metrics.
isNewlyInstalled = !new File("data-storage/Slimefun").exists();
// Creating all necessary Folders
logger.log(Level.INFO, "Creating directories...");
createDirectories();
// Load various config settings into our cache
registry.load(this, config);
// Set up localization
logger.log(Level.INFO, "Loading language files...");
String chatPrefix = config.getString("options.chat-prefix");
String serverDefaultLanguage = config.getString("options.language");
local = new LocalizationService(this, chatPrefix, serverDefaultLanguage);
int networkSize = config.getInt("networks.max-size");
// Make sure that the network size is a valid input
if (networkSize < 1) {
logger.log(Level.WARNING, "Your 'networks.max-size' setting is misconfigured! It must be at least 1, it was set to: {0}", networkSize);
networkSize = 1;
}
networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"), config.getBoolean("networks.delete-excess-items"));
// Data storage
playerStorage = new LegacyStorage();
logger.log(Level.INFO, "Using legacy storage for player data");
// Setting up bStats and analytics
new Thread(metricsService::start, "Slimefun Metrics").start();
analyticsService.start();
// Starting the Auto-Updater
if (config.getBoolean("options.auto-update")) {
logger.log(Level.INFO, "Starting Auto-Updater...");
updaterService.start();
} else {
updaterService.disable();
}
// Registering all GEO Resources
logger.log(Level.INFO, "Loading GEO-Resources...");
GEOResourcesSetup.setup();
logger.log(Level.INFO, "Loading Tags...");
loadTags();
logger.log(Level.INFO, "Loading items...");
loadItems();
logger.log(Level.INFO, "Loading researches...");
loadResearches();
registry.setResearchingEnabled(getResearchCfg().getBoolean("enable-researching"));
PostSetup.setupWiki();
logger.log(Level.INFO, "Registering listeners...");
registerListeners();
// Initiating various Stuff and all items with a slight delay (0ms after the Server finished loading)
runSync(new SlimefunStartupTask(this, () -> {
textureService.register(registry.getAllSlimefunItems(), true);
permissionsService.register(registry.getAllSlimefunItems(), true);
soundService.reload(true);
// This try/catch should prevent buggy Spigot builds from blocking item loading
try {
recipeService.refresh();
} catch (Exception | LinkageError x) {
logger.log(Level.SEVERE, x, () -> "An Exception occurred while iterating through the Recipe list on Minecraft Version " + minecraftVersion.getName() + " (Slimefun v" + getVersion() + ")");
}
}), 0);
// Setting up our commands
try {
command.register();
} catch (Exception | LinkageError x) {
logger.log(Level.SEVERE, "An Exception occurred while registering the /slimefun command", x);
}
// Armor Update Task
if (config.getBoolean("options.enable-armor-effects")) {
new SlimefunArmorTask().schedule(this, config.getInt("options.armor-update-interval") * 20L);
if (config.getBoolean("options.enable-radiation")) {
new RadiationTask().schedule(this, config.getInt("options.radiation-update-interval") * 20L);
}
new RainbowArmorTask().schedule(this, config.getInt("options.rainbow-armor-update-interval") * 20L);
new SolarHelmetTask().schedule(this, config.getInt("options.armor-update-interval"));
} else if (config.getBoolean("options.enable-radiation")) {
logger.log(Level.WARNING, "Cannot enable radiation while armor effects are disabled.");
}
// Starting our tasks
autoSavingService.start(this, config.getInt("options.auto-save-delay-in-minutes"));
hologramsService.start();
ticker.start(this);
// Loading integrations
logger.log(Level.INFO, "Loading Third-Party plugin integrations...");
integrations.start();
gitHubService.start(this);
// Hooray!
logger.log(Level.INFO, "Slimefun has finished loading in {0}", getStartupTime(timestamp));
}
@Override
public JavaPlugin getJavaPlugin() {
return this;
}
@Override
public String getBugTrackerURL() {
return "https://github.com/Slimefun/Slimefun4/issues";
}
/**
* This method gets called when the {@link Plugin} gets disabled.
* Most often it is called when the {@link Server} is shutting down or reloading.
*/
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) {
return;
}
// Cancel all tasks from this plugin immediately
Bukkit.getScheduler().cancelTasks(this);
// Finishes all started movements/removals of block data
try {
ticker.halt();
ticker.run();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "Something went wrong while disabling the ticker task for Slimefun v" + getDescription().getVersion());
}
// Kill our Profiler Threads
profiler.kill();
// Save all Player Profiles that are still in memory
PlayerProfile.iterator().forEachRemaining(profile -> {
if (profile.isDirty()) {
profile.save();
}
});
// Save all registered Worlds
for (Map.Entry<String, BlockStorage> entry : getRegistry().getWorlds().entrySet()) {
try {
entry.getValue().saveAndRemove();
} catch (Exception x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion());
}
}
// Save all "universal" inventories (ender chests for example)
for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) {
menu.save();
}
// Create a new backup zip
if (config.getBoolean("options.backup-data")) {
backupService.run();
}
// Close and unload any resources from our Metrics Service
metricsService.cleanUp();
// Terminate our Plugin instance
setInstance(null);
/**
* Close all inventories on the server to prevent item dupes
* (Incase some idiot uses /reload)
*/
for (Player p : Bukkit.getOnlinePlayers()) {
p.closeInventory();
}
}
/**
* This is a private internal method to set the de-facto instance of {@link Slimefun}.
* Having this as a seperate method ensures the seperation between static and non-static fields.
* It also makes sonarcloud happy :)
* Only ever use it during {@link #onEnable()} or {@link #onDisable()}.
*
* @param pluginInstance
* Our instance of {@link Slimefun} or null
*/
private static void setInstance(@Nullable Slimefun pluginInstance) {
instance = pluginInstance;
}
/**
* This returns the time it took to load Slimefun (given a starting point).
*
* @param timestamp
* The time at which we started to load Slimefun.
*
* @return The total time it took to load Slimefun (in ms or s)
*/
private @Nonnull String getStartupTime(long timestamp) {
long ms = (System.nanoTime() - timestamp) / 1000000;
if (ms > 1000) {
return NumberUtils.roundDecimalNumber(ms / 1000.0) + 's';
} else {
return NumberUtils.roundDecimalNumber(ms) + "ms";
}
}
/**
* This method checks if this is currently running in a unit test
* environment.
*
* @return Whether we are inside a unit test
*/
public boolean isUnitTest() {
return minecraftVersion == MinecraftVersion.UNIT_TEST;
}
/**
* This method checks for the {@link MinecraftVersion} of the {@link Server}.
* If the version is unsupported, a warning will be printed to the console.
*
* @return Whether the {@link MinecraftVersion} is unsupported
*/
private boolean isVersionUnsupported() {
try {
// First check if they still use the unsupported CraftBukkit software.
if (!PaperLib.isSpigot() && Bukkit.getName().equals("CraftBukkit")) {
StartupWarnings.invalidServerSoftware(getLogger());
return true;
}
// Now check the actual Version of Minecraft
int version = PaperLib.getMinecraftVersion();
int patchVersion = PaperLib.getMinecraftPatchVersion();
if (version > 0) {
// Check all supported versions of Minecraft
for (MinecraftVersion supportedVersion : MinecraftVersion.values()) {
if (supportedVersion.isMinecraftVersion(version, patchVersion)) {
minecraftVersion = supportedVersion;
return false;
}
}
// Looks like you are using an unsupported Minecraft Version
StartupWarnings.invalidMinecraftVersion(getLogger(), version, getDescription().getVersion());
return true;
} else {
getLogger().log(Level.WARNING, "We could not determine the version of Minecraft you were using? ({0})", Bukkit.getVersion());
/*
* If we are unsure about it, we will assume "supported".
* They could be using a non-Bukkit based Software which still
* might support Bukkit-based plugins.
* Use at your own risk in this case.
*/
return false;
}
} catch (Exception | LinkageError x) {
getLogger().log(Level.SEVERE, x, () -> "Error: Could not determine Environment or version of Minecraft for Slimefun v" + getDescription().getVersion());
// We assume "unsupported" if something went wrong.
return true;
}
}
/**
* This private method gives us a {@link Collection} of every {@link MinecraftVersion}
* that Slimefun is compatible with (as a {@link String} representation).
* <p>
* Example:
*
* <pre>
* { 1.14.x, 1.15.x, 1.16.x }
* </pre>
*
* @return A {@link Collection} of all compatible minecraft versions as strings
*/
static @Nonnull Collection<String> getSupportedVersions() {
List<String> list = new ArrayList<>();
for (MinecraftVersion version : MinecraftVersion.values()) {
if (!version.isVirtual()) {
list.add(version.getName());
}
}
return list;
}
/**
* This method creates all necessary directories (and sub directories) for Slimefun.
*/
private void createDirectories() {
String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" };
String[] pluginFolders = { "scripts", "error-reports", "cache/github", "world-settings" };
for (String folder : storageFolders) {
File file = new File("data-storage/Slimefun", folder);
if (!file.exists()) {
file.mkdirs();
}
}
for (String folder : pluginFolders) {
File file = new File("plugins/Slimefun", folder);
if (!file.exists()) {
file.mkdirs();
}
}
}
/**
* This method registers all of our {@link Listener Listeners}.
*/
private void registerListeners() {
// Old deprecated CS-CoreLib Listener
new MenuListener(this);
new SlimefunBootsListener(this);
new SlimefunItemInteractListener(this);
new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this);
new CargoNodeListener(this);
new MultiBlockListener(this);
new GadgetsListener(this);
new DispenserListener(this);
new BlockListener(this);
new EnhancedFurnaceListener(this);
new ItemPickupListener(this);
new ItemDropListener(this);
new DeathpointListener(this);
new ExplosionsListener(this);
new DebugFishListener(this);
new FireworksListener(this);
new WitherListener(this);
new IronGolemListener(this);
new EntityInteractionListener(this);
new MobDropListener(this);
new VillagerTradingListener(this);
new ElytraImpactListener(this);
new CraftingTableListener(this);
new AnvilListener(this);
new BrewingStandListener(this);
new CauldronListener(this);
new GrindstoneListener(this);
new CartographyTableListener(this);
new ButcherAndroidListener(this);
new MiningAndroidListener(this);
new NetworkListener(this, networkManager);
new HopperListener(this);
new TalismanListener(this);
new SoulboundListener(this);
new AutoCrafterListener(this);
new SlimefunItemHitListener(this);
new MiddleClickListener(this);
new BeeListener(this);
new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem());
new PiglinListener(this);
new SmithingTableListener(this);
new JoinListener(this);
new BundlingListener(this);
// Item-specific Listeners
new CoolerListener(this, (Cooler) SlimefunItems.COOLER.getItem());
new SeismicAxeListener(this, (SeismicAxe) SlimefunItems.SEISMIC_AXE.getItem());
new RadioactivityListener(this);
new AncientAltarListener(this, (AncientAltar) SlimefunItems.ANCIENT_ALTAR.getItem(), (AncientPedestal) SlimefunItems.ANCIENT_PEDESTAL.getItem());
grapplingHookListener.register(this, (GrapplingHook) SlimefunItems.GRAPPLING_HOOK.getItem());
bowListener.register(this);
backpackListener.register(this);
// Handle Slimefun Guide being given on Join
new SlimefunGuideListener(this, config.getBoolean("guide.receive-on-first-join"));
// Clear the Slimefun Guide History upon Player Leaving
new PlayerProfileListener(this);
}
/**
* This (re)loads every {@link SlimefunTag}.
*/
private void loadTags() {
for (SlimefunTag tag : SlimefunTag.values()) {
try {
// Only reload "empty" (or unloaded) Tags
if (tag.isEmpty()) {
tag.reload();
}
} catch (TagMisconfigurationException e) {
getLogger().log(Level.SEVERE, e, () -> "Failed to load Tag: " + tag.name());
}
}
}
/**
* This loads all of our items.
*/
private void loadItems() {
try {
SlimefunItemSetup.setup(this);
} catch (Exception | LinkageError x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while initializing SlimefunItems for Slimefun " + getVersion());
}
}
/**
* This loads our researches.
*/
private void loadResearches() {
try {
ResearchSetup.setupResearches();
} catch (Exception | LinkageError x) {
getLogger().log(Level.SEVERE, x, () -> "An Error occurred while initializing Slimefun Researches for Slimefun " + getVersion());
}
}
/**
* This returns the global instance of {@link Slimefun}.
* This may return null if the {@link Plugin} was disabled.
*
* @return The {@link Slimefun} instance
*/
public static @Nullable Slimefun instance() {
return instance;
}
/**
* This private static method allows us to throw a proper {@link Exception}
* whenever someone tries to access a static method while the instance is null.
* This happens when the method is invoked before {@link #onEnable()} or after {@link #onDisable()}.
* <p>
* Use it whenever a null check is needed to avoid a non-descriptive {@link NullPointerException}.
*/
private static void validateInstance() {
if (instance == null) {
throw new IllegalStateException("Cannot invoke static method, Slimefun instance is null.");
}
}
/**
* This returns the {@link Logger} instance that Slimefun uses.
* <p>
* <strong>Any {@link SlimefunAddon} should use their own {@link Logger} instance!</strong>
*
* @return Our {@link Logger} instance
*/
public static @Nonnull Logger logger() {
validateInstance();
return instance.getLogger();
}
/**
* This returns the version of Slimefun that is currently installed.
*
* @return The currently installed version of Slimefun
*/
public static @Nonnull String getVersion() {
validateInstance();
return instance.getDescription().getVersion();
}
public static @Nonnull Config getCfg() {
validateInstance();
return instance.config;
}
public static @Nonnull Config getResearchCfg() {
validateInstance();
return instance.researches;
}
public static @Nonnull Config getItemCfg() {
validateInstance();
return instance.items;
}
/**
* This returns our {@link GPSNetwork} instance.
* The {@link GPSNetwork} is responsible for handling any GPS-related
* operations and for managing any {@link GEOResource}.
*
* @return Our {@link GPSNetwork} instance
*/
public static @Nonnull GPSNetwork getGPSNetwork() {
validateInstance();
return instance.gpsNetwork;
}
public static @Nonnull TickerTask getTickerTask() {
validateInstance();
return instance.ticker;
}
/**
* This returns the {@link LocalizationService} of Slimefun.
*
* @return The {@link LocalizationService} of Slimefun
*/
public static @Nonnull LocalizationService getLocalization() {
validateInstance();
return instance.local;
}
/**
* This method returns out {@link MinecraftRecipeService} for Slimefun.
* This service is responsible for finding/identifying {@link Recipe Recipes}
* from vanilla Minecraft.
*
* @return Slimefun's {@link MinecraftRecipeService} instance
*/
public static @Nonnull MinecraftRecipeService getMinecraftRecipeService() {
validateInstance();
return instance.recipeService;
}
public static @Nonnull CustomItemDataService getItemDataService() {
validateInstance();
return instance.itemDataService;
}
public static @Nonnull CustomTextureService getItemTextureService() {
validateInstance();
return instance.textureService;
}
public static @Nonnull PermissionsService getPermissionsService() {
validateInstance();
return instance.permissionsService;
}
public static @Nonnull BlockDataService getBlockDataService() {
validateInstance();
return instance.blockDataService;
}
/**
* This method returns out world settings service.
* That service is responsible for managing item settings per
* {@link World}, such as disabling a {@link SlimefunItem} in a
* specific {@link World}.
*
* @return Our instance of {@link PerWorldSettingsService}
*/
public static @Nonnull PerWorldSettingsService getWorldSettingsService() {
validateInstance();
return instance.worldSettingsService;
}
/**
* This returns our {@link HologramsService} which handles the creation and
* cleanup of any holograms.
*
* @return Our instance of {@link HologramsService}
*/
public static @Nonnull HologramsService getHologramsService() {
validateInstance();
return instance.hologramsService;
}
/**
* This returns our {@link SoundService} which handles the configuration of all sounds used in Slimefun
*
* @return Our instance of {@link SoundService}
*/
@Nonnull
public static SoundService getSoundService() {
validateInstance();
return instance.soundService;
}
/**
* This returns our instance of {@link IntegrationsManager}.
* This is responsible for managing any integrations with third party {@link Plugin plugins}.
*
* @return Our instance of {@link IntegrationsManager}
*/
public static @Nonnull IntegrationsManager getIntegrations() {
validateInstance();
return instance.integrations;
}
/**
* This returns out instance of the {@link ProtectionManager}.
* This bridge is used to hook into any third-party protection {@link Plugin}.
*
* @return Our instanceof of the {@link ProtectionManager}
*/
public static @Nonnull ProtectionManager getProtectionManager() {
return getIntegrations().getProtectionManager();
}
/**
* This method returns the {@link UpdaterService} of Slimefun.
* It is used to handle automatic updates.
*
* @return The {@link UpdaterService} for Slimefun
*/
public static @Nonnull UpdaterService getUpdater() {
validateInstance();
return instance.updaterService;
}
/**
* This method returns the {@link MetricsService} of Slimefun.
* It is used to handle sending metric information to bStats.
*
* @return The {@link MetricsService} for Slimefun
*/
public static @Nonnull MetricsService getMetricsService() {
validateInstance();
return instance.metricsService;
}
/**
* This method returns the {@link AnalyticsService} of Slimefun.
* It is used to handle sending analytic information.
*
* @return The {@link AnalyticsService} for Slimefun
*/
public static @Nonnull AnalyticsService getAnalyticsService() {
validateInstance();
return instance.analyticsService;
}
/**
* This method returns the {@link GitHubService} of Slimefun.
* It is used to retrieve data from GitHub repositories.
*
* @return The {@link GitHubService} for Slimefun
*/
public static @Nonnull GitHubService getGitHubService() {
validateInstance();
return instance.gitHubService;
}
/**
* This returns our {@link NetworkManager} which is responsible
* for handling the Cargo and Energy networks.
*
* @return Our {@link NetworkManager} instance
*/
public static @Nonnull NetworkManager getNetworkManager() {
validateInstance();
return instance.networkManager;
}
public static @Nonnull SlimefunRegistry getRegistry() {
validateInstance();
return instance.registry;
}
public static @Nonnull GrapplingHookListener getGrapplingHookListener() {
validateInstance();
return instance.grapplingHookListener;
}
public static @Nonnull BackpackListener getBackpackListener() {
validateInstance();
return instance.backpackListener;
}
public static @Nonnull SlimefunBowListener getBowListener() {
validateInstance();
return instance.bowListener;
}
/**
* The {@link Command} that was added by Slimefun.
*
* @return Slimefun's command
*/
public static @Nonnull SlimefunCommand getCommand() {
validateInstance();
return instance.command;
}
/**
* This returns our instance of the {@link SlimefunProfiler}, a tool that is used
* to analyse performance and lag.
*
* @return The {@link SlimefunProfiler}
*/
public static @Nonnull SlimefunProfiler getProfiler() {
validateInstance();
return instance.profiler;
}
/**
* This returns the currently installed version of Minecraft.
*
* @return The current version of Minecraft
*/
public static @Nonnull MinecraftVersion getMinecraftVersion() {
validateInstance();
return instance.minecraftVersion;
}
/**
* This method returns whether this version of Slimefun was newly installed.
* It will return true if this {@link Server} uses Slimefun for the very first time.
*