2015-08-29 14:33:25 +02:00
/ *
2016-05-16 22:52:27 +02:00
* This file ( " WorldUtil.java " ) is part of the Actually Additions mod for Minecraft .
2015-08-29 14:33:25 +02:00
* It is created and owned by Ellpeck and distributed
* under the Actually Additions License to be found at
2016-05-16 22:52:27 +02:00
* http : //ellpeck.de/actaddlicense
2015-08-29 14:33:25 +02:00
* View the source code at https : //github.com/Ellpeck/ActuallyAdditions
*
2017-01-01 16:23:26 +01:00
* © 2015 - 2017 Ellpeck
2015-08-29 14:33:25 +02:00
* /
2016-01-05 04:47:35 +01:00
package de.ellpeck.actuallyadditions.mod.util ;
2015-04-19 01:50:02 +02:00
2016-07-25 02:07:16 +02:00
import de.ellpeck.actuallyadditions.mod.ActuallyAdditions ;
2021-02-27 21:24:26 +01:00
import de.ellpeck.actuallyadditions.mod.ActuallyAdditionsClient ;
2017-02-04 14:12:17 +01:00
import de.ellpeck.actuallyadditions.mod.tile.FilterSettings ;
import de.ellpeck.actuallyadditions.mod.util.compat.SlotlessableItemHandlerWrapper ;
2024-03-02 21:23:08 +01:00
import net.minecraft.core.BlockPos ;
import net.minecraft.core.Direction ;
import net.minecraft.server.level.ServerLevel ;
import net.minecraft.server.level.ServerPlayer ;
import net.minecraft.world.InteractionHand ;
import net.minecraft.world.entity.item.ItemEntity ;
import net.minecraft.world.entity.player.Player ;
import net.minecraft.world.item.ItemStack ;
import net.minecraft.world.item.Items ;
import net.minecraft.world.level.Level ;
import net.minecraft.world.level.block.Block ;
import net.minecraft.world.level.block.Blocks ;
import net.minecraft.world.level.block.entity.BlockEntity ;
import net.minecraft.world.level.block.state.BlockState ;
import net.minecraft.world.level.block.state.properties.BlockStateProperties ;
import net.minecraft.world.phys.BlockHitResult ;
import net.minecraft.world.phys.Vec3 ;
2024-03-04 20:21:48 +01:00
import net.neoforged.neoforge.capabilities.Capabilities ;
import net.neoforged.neoforge.common.CommonHooks ;
import net.neoforged.neoforge.common.NeoForge ;
import net.neoforged.neoforge.common.util.FakePlayer ;
import net.neoforged.neoforge.common.util.FakePlayerFactory ;
import net.neoforged.neoforge.energy.IEnergyStorage ;
2024-03-12 18:00:23 +01:00
import net.neoforged.neoforge.event.EventHooks ;
import net.neoforged.neoforge.event.entity.player.PlayerEvent ;
2024-03-04 20:21:48 +01:00
import net.neoforged.neoforge.event.level.BlockEvent.BreakEvent ;
import net.neoforged.neoforge.fluids.FluidStack ;
import net.neoforged.neoforge.fluids.capability.IFluidHandler ;
2021-02-26 22:15:48 +01:00
import java.util.ArrayList ;
2021-02-27 16:33:00 +01:00
import java.util.List ;
2015-06-28 03:12:32 +02:00
2018-01-29 08:17:31 +01:00
public final class WorldUtil {
2018-06-23 01:39:30 +02:00
public static boolean doItemInteraction ( SlotlessableItemHandlerWrapper extractWrapper , SlotlessableItemHandlerWrapper insertWrapper , int maxExtract ) {
return doItemInteraction ( extractWrapper , insertWrapper , maxExtract , null ) ;
}
public static boolean doItemInteraction ( SlotlessableItemHandlerWrapper extractWrapper , SlotlessableItemHandlerWrapper insertWrapper , int maxExtract , FilterSettings filter ) {
return doItemInteraction ( extractWrapper , insertWrapper , maxExtract , 0 , Integer . MAX_VALUE , 0 , Integer . MAX_VALUE , filter ) ;
}
public static boolean doItemInteraction ( SlotlessableItemHandlerWrapper extractWrapper , SlotlessableItemHandlerWrapper insertWrapper , int maxExtract , int extractSlotStart , int extractSlotEnd , int insertSlotStart , int insertSlotEnd , FilterSettings filter ) {
ItemStack theoreticalExtract = extractItem ( extractWrapper , maxExtract , true , extractSlotStart , extractSlotEnd , filter ) ;
if ( StackUtil . isValid ( theoreticalExtract ) ) {
ItemStack remaining = StackUtil . insertItem ( insertWrapper , theoreticalExtract , false , insertSlotStart , insertSlotEnd ) ;
2021-08-22 17:09:06 +02:00
if ( ! ItemStack . matches ( remaining , theoreticalExtract ) ) {
2018-06-23 01:39:30 +02:00
int toExtract = theoreticalExtract . getCount ( ) - remaining . getCount ( ) ;
extractItem ( extractWrapper , toExtract , false , extractSlotStart , extractSlotEnd , filter ) ;
return true ;
}
}
return false ;
}
public static ItemStack extractItem ( SlotlessableItemHandlerWrapper extractWrapper , int maxExtract , boolean simulate , int slotStart , int slotEnd , FilterSettings filter ) {
2022-06-24 21:38:07 +02:00
ItemStack extracted = ItemStack . EMPTY ;
2018-06-23 01:39:30 +02:00
if ( ActuallyAdditions . commonCapsLoaded ) {
2021-11-21 20:49:33 +01:00
/ * Object handler = extractWrapper . getSlotlessHandler ( ) ;
2018-06-23 01:39:30 +02:00
if ( handler instanceof ISlotlessItemHandler ) {
ISlotlessItemHandler slotless = ( ISlotlessItemHandler ) handler ;
if ( filter = = null | | ! filter . needsCheck ( ) ) {
extracted = slotless . extractItem ( maxExtract , simulate ) ;
return extracted ;
} else {
ItemStack would = slotless . extractItem ( maxExtract , true ) ;
if ( filter . check ( would ) ) {
if ( simulate ) {
extracted = would ;
} else {
extracted = slotless . extractItem ( maxExtract , false ) ;
}
}
//Leave the possibility to fall back to vanilla when there is a filter
}
2021-11-21 20:49:33 +01:00
} * /
2018-06-23 01:39:30 +02:00
}
if ( ! StackUtil . isValid ( extracted ) ) {
2021-11-21 20:49:33 +01:00
/ * IItemHandler handler = extractWrapper . getNormalHandler ( ) ;
2018-06-23 01:39:30 +02:00
if ( handler ! = null ) {
for ( int i = Math . max ( 0 , slotStart ) ; i < Math . min ( slotEnd , handler . getSlots ( ) ) ; i + + ) {
if ( filter = = null | | ! filter . needsCheck ( ) | | filter . check ( handler . getStackInSlot ( i ) ) ) {
extracted = handler . extractItem ( i , maxExtract , simulate ) ;
if ( StackUtil . isValid ( extracted ) ) {
break ;
}
}
}
2021-11-21 20:49:33 +01:00
} * /
2018-06-23 01:39:30 +02:00
}
return extracted ;
}
2024-03-04 20:21:48 +01:00
public static void doEnergyInteraction ( Level level , BlockPos posFrom , BlockPos posTo , Direction sideTo , int maxTransfer ) {
2018-06-23 01:39:30 +02:00
if ( maxTransfer > 0 ) {
2021-02-27 13:24:45 +01:00
Direction opp = sideTo = = null
2021-11-21 20:49:33 +01:00
? null
: sideTo . getOpposite ( ) ;
2024-03-04 20:21:48 +01:00
IEnergyStorage handlerFrom = level . getCapability ( Capabilities . EnergyStorage . BLOCK , posFrom , sideTo ) ;
IEnergyStorage handlerTo = level . getCapability ( Capabilities . EnergyStorage . BLOCK , posTo , opp ) ;
if ( handlerFrom ! = null & & handlerTo ! = null ) {
int drain = handlerFrom . extractEnergy ( maxTransfer , true ) ;
if ( drain > 0 ) {
int filled = handlerTo . receiveEnergy ( drain , false ) ;
handlerFrom . extractEnergy ( filled , false ) ;
}
}
2018-06-23 01:39:30 +02:00
}
}
2024-03-04 20:21:48 +01:00
public static void doFluidInteraction ( Level level , BlockPos posFrom , BlockPos posTo , Direction sideTo , int maxTransfer ) {
2018-06-23 01:39:30 +02:00
if ( maxTransfer > 0 ) {
2024-03-04 20:21:48 +01:00
IFluidHandler handlerFrom = level . getCapability ( Capabilities . FluidHandler . BLOCK , posFrom , sideTo ) ;
IFluidHandler handlerTo = level . getCapability ( Capabilities . FluidHandler . BLOCK , posTo , sideTo . getOpposite ( ) ) ;
if ( handlerFrom ! = null & & handlerTo ! = null ) {
FluidStack drain = handlerFrom . drain ( maxTransfer , IFluidHandler . FluidAction . SIMULATE ) ;
if ( ! drain . isEmpty ( ) ) {
int filled = handlerTo . fill ( drain . copy ( ) , IFluidHandler . FluidAction . EXECUTE ) ;
handlerFrom . drain ( filled , IFluidHandler . FluidAction . EXECUTE ) ;
}
}
2018-06-23 01:39:30 +02:00
}
}
/ * *
* Checks if a given Block with a given Meta is present in given Positions
*
* @param positions The Positions , an array of { x , y , z } arrays containing Positions
* @param block The Block
2024-03-02 21:23:08 +01:00
* @param level The World
2018-06-23 01:39:30 +02:00
* @return Is every block present ?
* /
2024-03-02 21:23:08 +01:00
public static boolean hasBlocksInPlacesGiven ( BlockPos [ ] positions , Block block , Level level ) {
2018-06-23 01:39:30 +02:00
for ( BlockPos pos : positions ) {
2024-03-02 21:23:08 +01:00
BlockState state = level . getBlockState ( pos ) ;
2021-02-27 16:33:00 +01:00
if ( ! ( state . getBlock ( ) = = block ) ) {
2021-02-26 22:15:48 +01:00
return false ;
}
2018-06-23 01:39:30 +02:00
}
return true ;
}
2024-03-02 21:23:08 +01:00
public static ItemStack useItemAtSide ( Direction side , Level level , BlockPos pos , ItemStack stack ) {
if ( level instanceof ServerLevel & & StackUtil . isValid ( stack ) & & pos ! = null ) {
2021-08-22 17:09:06 +02:00
BlockPos offsetPos = pos . relative ( side ) ;
2024-03-02 21:23:08 +01:00
BlockState state = level . getBlockState ( offsetPos ) ;
2018-06-23 01:39:30 +02:00
Block block = state . getBlock ( ) ;
2021-11-21 20:49:33 +01:00
boolean replaceable = false ; //= block.canBeReplaced(world, offsetPos); //TODO
2018-06-23 01:39:30 +02:00
//Redstone
if ( replaceable & & stack . getItem ( ) = = Items . REDSTONE ) {
2024-03-02 21:23:08 +01:00
level . setBlock ( offsetPos , Blocks . REDSTONE_WIRE . defaultBlockState ( ) , 2 ) ;
2018-06-23 01:39:30 +02:00
return StackUtil . shrink ( stack , 1 ) ;
}
//Plants
2021-11-21 20:49:33 +01:00
/ * if ( replaceable & & stack . getItem ( ) instanceof IPlantable ) { //TODO
2018-06-23 01:39:30 +02:00
if ( ( ( IPlantable ) stack . getItem ( ) ) . getPlant ( world , offsetPos ) . getBlock ( ) . canPlaceBlockAt ( world , offsetPos ) ) {
2021-08-22 17:09:06 +02:00
if ( world . setBlock ( offsetPos , ( ( IPlantable ) stack . getItem ( ) ) . getPlant ( world , offsetPos ) , 2 ) ) {
2021-02-26 22:15:48 +01:00
return StackUtil . shrink ( stack , 1 ) ;
}
2018-06-23 01:39:30 +02:00
}
2021-11-21 20:49:33 +01:00
} * /
2018-06-23 01:39:30 +02:00
//Everything else
try {
2024-03-02 21:23:08 +01:00
FakePlayer fake = FakePlayerFactory . getMinecraft ( ( ServerLevel ) level ) ;
2024-03-04 21:38:02 +01:00
// if (fake.connection == null) {
// fake.connection = new NetHandlerSpaghettiServer(fake);
// }
2022-04-03 22:18:46 +02:00
//ItemStack heldBefore = fake.getMainHandItem();
2024-03-02 21:23:08 +01:00
setHandItemWithoutAnnoyingSound ( fake , InteractionHand . MAIN_HAND , stack . copy ( ) ) ;
BlockHitResult ray = new BlockHitResult ( new Vec3 ( 0 . 5 , 0 . 5 , 0 . 5 ) , side . getOpposite ( ) , offsetPos , true ) ;
fake . gameMode . useItemOn ( fake , level , fake . getMainHandItem ( ) , InteractionHand . MAIN_HAND , ray ) ;
ItemStack result = fake . getItemInHand ( InteractionHand . MAIN_HAND ) ;
2022-04-03 22:18:46 +02:00
//setHandItemWithoutAnnoyingSound(fake, Hand.MAIN_HAND, heldBefore);
2018-06-29 00:24:51 +02:00
return result ;
2018-06-23 01:39:30 +02:00
} catch ( Exception e ) {
2024-03-02 21:23:08 +01:00
ActuallyAdditions . LOGGER . error ( " Something that places Blocks at " + offsetPos . getX ( ) + " , " + offsetPos . getY ( ) + " , " + offsetPos . getZ ( ) + " in World " + level . dimension ( ) + " threw an Exception! Don't let that happen again! " , e ) ;
2018-06-23 01:39:30 +02:00
}
}
return stack ;
}
2024-03-02 21:23:08 +01:00
public static boolean dropItemAtSide ( Direction side , Level level , BlockPos pos , ItemStack stack ) {
2021-08-22 17:09:06 +02:00
BlockPos coords = pos . relative ( side ) ;
2024-03-02 21:23:08 +01:00
if ( level . hasChunkAt ( coords ) ) {
ItemEntity item = new ItemEntity ( level , coords . getX ( ) + 0 . 5 , coords . getY ( ) + 0 . 5 , coords . getZ ( ) + 0 . 5 , stack ) ;
2021-11-21 20:49:33 +01:00
item . setDeltaMovement ( 0 , 0 , 0 ) ;
2018-06-23 01:39:30 +02:00
2024-03-02 21:23:08 +01:00
return level . addFreshEntity ( item ) ;
2018-06-23 01:39:30 +02:00
}
return false ;
}
2021-02-27 13:24:45 +01:00
public static Direction getDirectionBySidesInOrder ( int side ) {
2018-06-23 01:39:30 +02:00
switch ( side ) {
2021-02-26 22:15:48 +01:00
case 0 :
2021-02-27 13:24:45 +01:00
return Direction . UP ;
2021-02-26 22:15:48 +01:00
case 1 :
2021-02-27 13:24:45 +01:00
return Direction . DOWN ;
2021-02-26 22:15:48 +01:00
case 2 :
2021-02-27 13:24:45 +01:00
return Direction . NORTH ;
2021-02-26 22:15:48 +01:00
case 3 :
2021-02-27 13:24:45 +01:00
return Direction . EAST ;
2021-02-26 22:15:48 +01:00
case 4 :
2021-02-27 13:24:45 +01:00
return Direction . SOUTH ;
2021-02-26 22:15:48 +01:00
default :
2021-02-27 13:24:45 +01:00
return Direction . WEST ;
2018-06-23 01:39:30 +02:00
}
}
2021-02-27 13:24:45 +01:00
public static Direction getDirectionByPistonRotation ( BlockState state ) {
2021-11-21 20:49:33 +01:00
return state . getValue ( BlockStateProperties . FACING ) ;
2018-06-23 01:39:30 +02:00
}
2024-03-03 01:20:53 +01:00
public static ArrayList < BlockState > getStatesAround ( Level level , BlockPos pos ) {
ArrayList < BlockState > blocks = new ArrayList < > ( ) ;
blocks . add ( level . getBlockState ( pos . relative ( Direction . NORTH ) ) ) ;
blocks . add ( level . getBlockState ( pos . relative ( Direction . EAST ) ) ) ;
blocks . add ( level . getBlockState ( pos . relative ( Direction . SOUTH ) ) ) ;
blocks . add ( level . getBlockState ( pos . relative ( Direction . WEST ) ) ) ;
2018-06-23 01:39:30 +02:00
return blocks ;
}
2024-03-02 21:23:08 +01:00
public static void setHandItemWithoutAnnoyingSound ( Player player , InteractionHand hand , ItemStack stack ) {
if ( hand = = InteractionHand . MAIN_HAND ) {
player . getInventory ( ) . items . set ( player . getInventory ( ) . selected , stack ) ;
} else if ( hand = = InteractionHand . OFF_HAND ) {
player . getInventory ( ) . offhand . set ( 0 , stack ) ;
2018-06-23 01:39:30 +02:00
}
}
//I think something is up with this, but I'm not entirely certain what.
2024-03-02 21:23:08 +01:00
public static float fireFakeHarvestEventsForDropChance ( BlockEntity caller , List < ItemStack > drops , Level level , BlockPos pos ) {
if ( level instanceof ServerLevel ) {
FakePlayer fake = FakePlayerFactory . getMinecraft ( ( ServerLevel ) level ) ;
2021-08-22 17:09:06 +02:00
BlockPos tePos = caller . getBlockPos ( ) ;
fake . setPos ( tePos . getX ( ) + 0 . 5 , tePos . getY ( ) + 0 . 5 , tePos . getZ ( ) + 0 . 5 ) ;
2024-03-02 21:23:08 +01:00
BlockState state = level . getBlockState ( pos ) ;
2018-06-23 01:39:30 +02:00
2024-03-02 21:23:08 +01:00
BreakEvent event = new BreakEvent ( level , pos , state , fake ) ;
2024-03-12 18:00:23 +01:00
NeoForge . EVENT_BUS . post ( event ) ;
if ( ! event . isCanceled ( ) ) {
return EventHooks . doPlayerHarvestCheck ( fake , state , true ) ? 1F : 0F ;
2021-11-21 20:49:33 +01:00
//return ForgeEventFactory.fireBlockHarvesting(drops, world, pos, state, 0, 1, false, fake); //TODO what?!
2021-02-26 22:15:48 +01:00
}
2018-06-23 01:39:30 +02:00
}
return 0F ;
}
/ * *
* Tries to break a block as if this player had broken it . This is a complex operation .
2021-02-26 22:15:48 +01:00
*
* @param stack The player ' s current held stack , main hand .
2024-03-02 21:23:08 +01:00
* @param level The player ' s world .
2018-06-23 01:39:30 +02:00
* @param player The player that is breaking this block .
2021-02-26 22:15:48 +01:00
* @param pos The pos to break .
2018-06-23 01:39:30 +02:00
* @return If the break was successful .
* /
2024-03-02 21:23:08 +01:00
public static boolean breakExtraBlock ( ItemStack stack , Level level , Player player , BlockPos pos ) {
BlockState state = level . getBlockState ( pos ) ;
2018-06-23 01:39:30 +02:00
Block block = state . getBlock ( ) ;
2021-02-28 15:47:54 +01:00
if ( player . isCreative ( ) ) {
2024-03-02 21:23:08 +01:00
if ( block . onDestroyedByPlayer ( state , level , pos , player , false , state . getFluidState ( ) ) ) {
block . destroy ( level , pos , state ) ;
2018-06-23 01:39:30 +02:00
}
// send update to client
2024-03-02 21:23:08 +01:00
if ( ! level . isClientSide ) {
2021-11-21 20:49:33 +01:00
//((ServerPlayerEntity) player).connection.send(new SPacketBlockChange(world, pos)); //TODO dunno what this is
2018-06-23 01:39:30 +02:00
}
return true ;
}
// callback to the tool the player uses. Called on both sides. This damages the tool n stuff.
2024-03-02 21:23:08 +01:00
stack . mineBlock ( level , state , pos , player ) ;
2018-06-23 01:39:30 +02:00
// server sided handling
2024-03-02 21:23:08 +01:00
if ( ! level . isClientSide ) {
2018-06-23 01:39:30 +02:00
// send the blockbreak event
2024-03-04 20:21:48 +01:00
int xp = CommonHooks . onBlockBreakEvent ( level , ( ( ServerPlayer ) player ) . gameMode . getGameModeForPlayer ( ) , ( ServerPlayer ) player , pos ) ;
2021-02-26 22:15:48 +01:00
if ( xp = = - 1 ) {
return false ;
}
2018-06-23 01:39:30 +02:00
2024-03-02 21:23:08 +01:00
BlockEntity blockEntity = level . getBlockEntity ( pos ) ;
if ( block . onDestroyedByPlayer ( state , level , pos , player , true , state . getFluidState ( ) ) ) { // boolean is if block can be harvested, checked above
block . destroy ( level , pos , state ) ;
block . playerDestroy ( level , player , pos , state , blockEntity , stack ) ;
block . popExperience ( ( ( ServerLevel ) level ) , pos , xp ) ;
2018-06-23 01:39:30 +02:00
}
// always send block update to client
2021-11-21 20:49:33 +01:00
//((ServerPlayerEntity) player).connection.send(new SPacketBlockChange(world, pos)); //TODO how
2018-06-23 01:39:30 +02:00
return true ;
}
// client sided handling
else {
// clientside we do a "this block has been clicked on long enough to be broken" call. This should not send any new packets
// the code above, executed on the server, sends a block-updates that give us the correct state of the block we destroy.
// following code can be found in PlayerControllerMP.onPlayerDestroyBlock
2024-03-02 21:23:08 +01:00
level . levelEvent ( 2001 , pos , Block . getId ( state ) ) ;
if ( block . onDestroyedByPlayer ( state , level , pos , player , true , state . getFluidState ( ) ) ) {
block . destroy ( level , pos , state ) ;
2018-06-23 01:39:30 +02:00
}
// callback to the tool
2024-03-02 21:23:08 +01:00
stack . mineBlock ( level , state , pos , player ) ;
2018-06-23 01:39:30 +02:00
// send an update to the server, so we get an update back
2021-02-27 21:24:26 +01:00
ActuallyAdditionsClient . sendBreakPacket ( pos ) ;
2018-06-23 01:39:30 +02:00
return true ;
}
}
2015-04-19 01:50:02 +02:00
}