--- a/net/minecraft/world/server/ServerWorld.java
+++ b/net/minecraft/world/server/ServerWorld.java
@@ -143,7 +143,7 @@
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-public class ServerWorld extends World implements ISeedReader {
+public class ServerWorld extends World implements ISeedReader, net.minecraftforge.common.extensions.IForgeWorldServer {
    public static final BlockPos field_241108_a_ = new BlockPos(100, 50, 0);
    private static final Logger field_147491_a = LogManager.getLogger();
    private final Int2ObjectMap<Entity> field_217498_x = new Int2ObjectLinkedOpenHashMap<>();
@@ -173,6 +173,7 @@
    private final DragonFightManager field_241105_O_;
    private final StructureManager field_241106_P_;
    private final boolean field_241107_Q_;
+   private net.minecraftforge.common.util.WorldCapabilityData capabilityData;
 
    public ServerWorld(MinecraftServer p_i241885_1_, Executor p_i241885_2_, SaveFormat.LevelSave p_i241885_3_, IServerWorldInfo p_i241885_4_, RegistryKey<World> p_i241885_5_, DimensionType p_i241885_6_, IChunkStatusListener p_i241885_7_, ChunkGenerator p_i241885_8_, boolean p_i241885_9_, long p_i241885_10_, List<ISpecialSpawner> p_i241885_12_, boolean p_i241885_13_) {
       super(p_i241885_4_, p_i241885_5_, p_i241885_6_, p_i241885_1_::func_213185_aS, false, p_i241885_9_, p_i241885_10_);
@@ -200,7 +201,7 @@
       } else {
          this.field_241105_O_ = null;
       }
-
+      this.initCapabilities();
    }
 
    public void func_241113_a_(int p_241113_1_, int p_241113_2_, boolean p_241113_3_, boolean p_241113_4_) {
@@ -296,15 +297,19 @@
          this.field_73061_a.func_184103_al().func_232642_a_(new SChangeGameStatePacket(SChangeGameStatePacket.field_241772_i_, this.field_73017_q), this.func_234923_W_());
       }
 
+      /* The function in use here has been replaced in order to only send the weather info to players in the correct dimension,
+       * rather than to all players on the server. This is what causes the client-side rain, as the
+       * client believes that it has started raining locally, rather than in another dimension.
+       */
       if (flag != this.func_72896_J()) {
          if (flag) {
-            this.field_73061_a.func_184103_al().func_148540_a(new SChangeGameStatePacket(SChangeGameStatePacket.field_241766_c_, 0.0F));
+            this.field_73061_a.func_184103_al().func_232642_a_(new SChangeGameStatePacket(SChangeGameStatePacket.field_241766_c_, 0.0F), this.func_234923_W_());
          } else {
-            this.field_73061_a.func_184103_al().func_148540_a(new SChangeGameStatePacket(SChangeGameStatePacket.field_241765_b_, 0.0F));
+            this.field_73061_a.func_184103_al().func_232642_a_(new SChangeGameStatePacket(SChangeGameStatePacket.field_241765_b_, 0.0F), this.func_234923_W_());
          }
 
-         this.field_73061_a.func_184103_al().func_148540_a(new SChangeGameStatePacket(SChangeGameStatePacket.field_241771_h_, this.field_73004_o));
-         this.field_73061_a.func_184103_al().func_148540_a(new SChangeGameStatePacket(SChangeGameStatePacket.field_241772_i_, this.field_73017_q));
+         this.field_73061_a.func_184103_al().func_232642_a_(new SChangeGameStatePacket(SChangeGameStatePacket.field_241771_h_, this.field_73004_o), this.func_234923_W_());
+         this.field_73061_a.func_184103_al().func_232642_a_(new SChangeGameStatePacket(SChangeGameStatePacket.field_241772_i_, this.field_73017_q), this.func_234923_W_());
       }
 
       if (this.field_73068_P && this.field_217491_A.stream().noneMatch((p_241132_0_) -> {
@@ -312,8 +317,8 @@
       })) {
          this.field_73068_P = false;
          if (this.func_82736_K().func_223586_b(GameRules.field_223607_j)) {
-            long l = this.field_72986_A.func_76073_f() + 24000L;
-            this.func_241114_a_(l - l % 24000L);
+            long l = this.func_72820_D() + 24000L;
+            this.func_241114_a_(net.minecraftforge.event.ForgeEventFactory.onSleepFinished(this, l - l % 24000L, this.func_72820_D()));
          }
 
          this.func_229856_ab_();
@@ -404,7 +409,7 @@
             if (entity1.field_70128_L) {
                this.func_217454_n(entity1);
                objectiterator.remove();
-               this.func_217484_g(entity1);
+               this.removeEntityComplete(entity1, entity1 instanceof ServerPlayerEntity); //Forge: Keep cap data until revive. Every other entity removes directly.
             }
 
             iprofiler.func_76319_b();
@@ -475,6 +480,7 @@
          BlockPos blockpos2 = this.func_205770_a(Heightmap.Type.MOTION_BLOCKING, this.func_217383_a(i, 0, j, 15));
          BlockPos blockpos3 = blockpos2.func_177977_b();
          Biome biome = this.func_226691_t_(blockpos2);
+         if (this.isAreaLoaded(blockpos2, 1)) // Forge: check area to avoid loading neighbors in unloaded chunks
          if (biome.func_201848_a(this, blockpos3)) {
             this.func_175656_a(blockpos3, Blocks.field_150432_aD.func_176223_P());
          }
@@ -598,9 +604,10 @@
             ++p_217479_1_.field_70173_aa;
             IProfiler iprofiler = this.func_217381_Z();
             iprofiler.func_194340_a(() -> {
-               return Registry.field_212629_r.func_177774_c(p_217479_1_.func_200600_R()).toString();
+               return p_217479_1_.func_200600_R().getRegistryName() == null ? p_217479_1_.func_200600_R().toString() : p_217479_1_.func_200600_R().getRegistryName().toString();
             });
             iprofiler.func_230035_c_("tickNonPassenger");
+            if (p_217479_1_.canUpdate())
             p_217479_1_.func_70071_h_();
             iprofiler.func_76319_b();
          }
@@ -687,6 +694,7 @@
             p_217445_1_.func_200209_c(new TranslationTextComponent("menu.savingChunks"));
          }
 
+         net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Save(this));
          serverchunkprovider.func_217210_a(p_217445_2_);
       }
    }
@@ -777,6 +785,7 @@
    }
 
    private void func_217448_f(ServerPlayerEntity p_217448_1_) {
+      if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_217448_1_, this))) return;
       Entity entity = this.field_175741_N.get(p_217448_1_.func_110124_au());
       if (entity != null) {
          field_147491_a.warn("Force-added player with duplicate UUID {}", (Object)p_217448_1_.func_110124_au().toString());
@@ -801,6 +810,7 @@
       } else if (this.func_217478_l(p_72838_1_)) {
          return false;
       } else {
+         if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_72838_1_, this))) return false;
          IChunk ichunk = this.func_217353_a(MathHelper.func_76128_c(p_72838_1_.func_226277_ct_() / 16.0D), MathHelper.func_76128_c(p_72838_1_.func_226281_cx_() / 16.0D), ChunkStatus.field_222617_m, p_72838_1_.field_98038_p);
          if (!(ichunk instanceof Chunk)) {
             return false;
@@ -816,6 +826,7 @@
       if (this.func_217478_l(p_217440_1_)) {
          return false;
       } else {
+         if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_217440_1_, this))) return false;
          this.func_217465_m(p_217440_1_);
          return true;
       }
@@ -879,12 +890,17 @@
 
    }
 
+   @Deprecated //Forge: Use removeEntityComplete(entity,boolean)
    public void func_217484_g(Entity p_217484_1_) {
+      removeEntityComplete(p_217484_1_, false);
+   }
+   public void removeEntityComplete(Entity p_217484_1_, boolean keepData) {
       if (p_217484_1_ instanceof EnderDragonEntity) {
          for(EnderDragonPartEntity enderdragonpartentity : ((EnderDragonEntity)p_217484_1_).func_213404_dT()) {
-            enderdragonpartentity.func_70106_y();
+            enderdragonpartentity.remove(keepData);
          }
       }
+      p_217484_1_.remove(keepData);
 
       this.field_175741_N.remove(p_217484_1_.func_110124_au());
       this.func_72863_F().func_217226_b(p_217484_1_);
@@ -898,6 +914,8 @@
          this.field_217495_I.remove(((MobEntity)p_217484_1_).func_70661_as());
       }
 
+      p_217484_1_.onRemovedFromWorld();
+      net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityLeaveWorldEvent(p_217484_1_, this));
    }
 
    private void func_217465_m(Entity p_217465_1_) {
@@ -918,15 +936,19 @@
          }
       }
 
+      p_217465_1_.onAddedToWorld();
    }
 
    public void func_217467_h(Entity p_217467_1_) {
+      removeEntity(p_217467_1_, false);
+   }
+   public void removeEntity(Entity p_217467_1_, boolean keepData) {
       if (this.field_217492_a) {
          throw (IllegalStateException)Util.func_229757_c_(new IllegalStateException("Removing entity while ticking!"));
       } else {
          this.func_217454_n(p_217467_1_);
          this.field_217498_x.remove(p_217467_1_.func_145782_y());
-         this.func_217484_g(p_217467_1_);
+         this.removeEntityComplete(p_217467_1_, keepData);
       }
    }
 
@@ -939,8 +961,11 @@
    }
 
    public void func_217434_e(ServerPlayerEntity p_217434_1_) {
-      p_217434_1_.func_70106_y();
-      this.func_217467_h(p_217434_1_);
+      removePlayer(p_217434_1_, false);
+   }
+   public void removePlayer(ServerPlayerEntity p_217434_1_, boolean keepData) {
+      p_217434_1_.remove(keepData);
+      this.removeEntity(p_217434_1_, keepData);
       this.func_72854_c();
    }
 
@@ -959,10 +984,20 @@
    }
 
    public void func_184148_a(@Nullable PlayerEntity p_184148_1_, double p_184148_2_, double p_184148_4_, double p_184148_6_, SoundEvent p_184148_8_, SoundCategory p_184148_9_, float p_184148_10_, float p_184148_11_) {
+      net.minecraftforge.event.entity.PlaySoundAtEntityEvent event = net.minecraftforge.event.ForgeEventFactory.onPlaySoundAtEntity(p_184148_1_, p_184148_8_, p_184148_9_, p_184148_10_, p_184148_11_);
+      if (event.isCanceled() || event.getSound() == null) return;
+      p_184148_8_ = event.getSound();
+      p_184148_9_ = event.getCategory();
+      p_184148_10_ = event.getVolume();
       this.field_73061_a.func_184103_al().func_148543_a(p_184148_1_, p_184148_2_, p_184148_4_, p_184148_6_, p_184148_10_ > 1.0F ? (double)(16.0F * p_184148_10_) : 16.0D, this.func_234923_W_(), new SPlaySoundEffectPacket(p_184148_8_, p_184148_9_, p_184148_2_, p_184148_4_, p_184148_6_, p_184148_10_, p_184148_11_));
    }
 
    public void func_217384_a(@Nullable PlayerEntity p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, SoundCategory p_217384_4_, float p_217384_5_, float p_217384_6_) {
+      net.minecraftforge.event.entity.PlaySoundAtEntityEvent event = net.minecraftforge.event.ForgeEventFactory.onPlaySoundAtEntity(p_217384_1_, p_217384_3_, p_217384_4_, p_217384_5_, p_217384_6_);
+      if (event.isCanceled() || event.getSound() == null) return;
+      p_217384_3_ = event.getSound();
+      p_217384_4_ = event.getCategory();
+      p_217384_5_ = event.getVolume();
       this.field_73061_a.func_184103_al().func_148543_a(p_217384_1_, p_217384_2_.func_226277_ct_(), p_217384_2_.func_226278_cu_(), p_217384_2_.func_226281_cx_(), p_217384_5_ > 1.0F ? (double)(16.0F * p_217384_5_) : 16.0D, this.func_234923_W_(), new SSpawnMovingSoundEffectPacket(p_217384_3_, p_217384_4_, p_217384_2_, p_217384_5_, p_217384_6_));
    }
 
@@ -998,6 +1033,7 @@
 
    public Explosion func_230546_a_(@Nullable Entity p_230546_1_, @Nullable DamageSource p_230546_2_, @Nullable ExplosionContext p_230546_3_, double p_230546_4_, double p_230546_6_, double p_230546_8_, float p_230546_10_, boolean p_230546_11_, Explosion.Mode p_230546_12_) {
       Explosion explosion = new Explosion(this, p_230546_1_, p_230546_2_, p_230546_3_, p_230546_4_, p_230546_6_, p_230546_8_, p_230546_10_, p_230546_11_, p_230546_12_);
+      if (net.minecraftforge.event.ForgeEventFactory.onExplosionStart(this, explosion)) return explosion;
       explosion.func_77278_a();
       explosion.func_77279_a(false);
       if (p_230546_12_ == Explosion.Mode.NONE) {
@@ -1410,4 +1446,14 @@
          p_241121_0_.func_175656_a(p_241122_1_, Blocks.field_150343_Z.func_176223_P());
       });
    }
+
+   protected void initCapabilities() {
+      this.gatherCapabilities();
+      capabilityData = this.func_217481_x().func_215752_a(() -> new net.minecraftforge.common.util.WorldCapabilityData(getCapabilities()), net.minecraftforge.common.util.WorldCapabilityData.ID);
+      capabilityData.setCapabilities(getCapabilities());
+   }
+
+   public java.util.stream.Stream<Entity> getEntities() {
+       return field_217498_x.values().stream();
+   }
 }
