mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Checkpoint resident crouching and pose integration in progress
Back up the current Rust pose, collision, ledge and native oracle work before further integration. This is a work-in-progress checkpoint: probe output, camera integration and differential validation are still pending; full vanilla parity is not claimed.
This commit is contained in:
1 parent
24ce21abee
commit
20714b5d77
6 files changed
+144
-32
No files matched your search
@@ -341,10 +341,10 @@ impl BlockLandingWorld for MotionWorld<'_, '_, '_> {
|
||||
Ok(true)
|
||||
}
|
||||
fn entity_dimensions(&self) -> (f32, f32) {
|
||||
(0.6, 1.8)
|
||||
(self.player.dimensions.width, self.player.dimensions.height)
|
||||
}
|
||||
fn suppressing_bounce(&mut self) -> Result<bool> {
|
||||
Ok(false)
|
||||
Ok(self.player.shift_key_down)
|
||||
}
|
||||
fn damage_source(
|
||||
&mut self,
|
||||
|
||||
@@ -9,6 +9,7 @@ pub mod input;
|
||||
pub mod landing;
|
||||
pub mod models;
|
||||
pub mod motion;
|
||||
pub mod pose;
|
||||
pub mod sprint;
|
||||
pub mod sprites;
|
||||
pub mod world;
|
||||
|
||||
@@ -14,6 +14,7 @@ use horizon_world::{
|
||||
collision_context::{CollisionContext, CollisionEntity, CollisionItem},
|
||||
entity_move::{self, CollisionFlags, EntityMoveWorld, MovementHistory, MoverType},
|
||||
entity_obstruction::EntityIdentity,
|
||||
edge_backoff::{self, EdgeBackoffInput, EdgeBackoffWorld},
|
||||
ground_support::{GroundState, find_supporting_block},
|
||||
living_air::{self, LivingAirWorld},
|
||||
living_fall::ImpulseContext,
|
||||
@@ -135,6 +136,10 @@ pub struct PlayerMotion {
|
||||
pub jump_trigger_time: i32,
|
||||
pub sprinting: bool,
|
||||
pub sprint_input: crate::sprint::SprintState,
|
||||
pub shift_key_down: bool,
|
||||
pub local_crouching: bool,
|
||||
pub pose: crate::pose::Pose,
|
||||
pub dimensions: crate::pose::Dimensions,
|
||||
pub speed: f32,
|
||||
pub last_climbable: Option<BlockPos>,
|
||||
pub needs_sync: bool,
|
||||
@@ -186,6 +191,10 @@ impl PlayerMotion {
|
||||
jump_trigger_time: 0,
|
||||
sprinting: false,
|
||||
sprint_input: crate::sprint::SprintState::default(),
|
||||
shift_key_down: false,
|
||||
local_crouching: false,
|
||||
pose: crate::pose::Pose::Standing,
|
||||
dimensions: crate::pose::Pose::Standing.dimensions(1.0),
|
||||
speed: 0.0,
|
||||
last_climbable: None,
|
||||
needs_sync: false,
|
||||
@@ -195,19 +204,7 @@ impl PlayerMotion {
|
||||
})
|
||||
}
|
||||
pub fn bounds(&self) -> Aabb {
|
||||
let half = f64::from(0.6_f32 / 2.0);
|
||||
Aabb::new(
|
||||
Vec3::new(
|
||||
self.position.x - half,
|
||||
self.position.y,
|
||||
self.position.z - half,
|
||||
),
|
||||
Vec3::new(
|
||||
self.position.x + half,
|
||||
self.position.y + f64::from(1.8_f32),
|
||||
self.position.z + half,
|
||||
),
|
||||
)
|
||||
self.dimensions.bounds(self.position)
|
||||
}
|
||||
pub fn attribute(&mut self, name: &str) -> Result<f64> {
|
||||
let key = self
|
||||
@@ -256,6 +253,7 @@ impl PlayerMotion {
|
||||
on_ground: self.ground.on_ground,
|
||||
horizontal_collision: self.collision.horizontal,
|
||||
minor_horizontal_collision: self.collision.minor,
|
||||
moving_slowly: self.moving_slowly(),
|
||||
..Default::default()
|
||||
};
|
||||
// The sprint setter edits attributes and flags, independent of local
|
||||
@@ -304,7 +302,7 @@ impl MotionWorld<'_, '_, '_> {
|
||||
CollisionEntity {
|
||||
identity: EntityIdentity(0),
|
||||
y: self.player.position.y,
|
||||
descending: false,
|
||||
descending: self.player.shift_key_down,
|
||||
main_hand: Some(CollisionItem::EMPTY),
|
||||
},
|
||||
false,
|
||||
@@ -316,6 +314,12 @@ impl MotionWorld<'_, '_, '_> {
|
||||
binding: self.collision_binding,
|
||||
}
|
||||
}
|
||||
pub(crate) fn no_player_collision(&mut self, bounds:Aabb)->Result<bool> {
|
||||
let context=self.context();
|
||||
Ok(world_movement::no_world_collision(&mut self.collisions(),world_movement::CollisionCheckRequest {
|
||||
entity:EntityIdentity(0),context,bounds,candidate_budget:1_000_000,
|
||||
})?)
|
||||
}
|
||||
fn block(&self, p: BlockPos) -> BlockStateId {
|
||||
self.scene.block(p.x, p.y, p.z)
|
||||
}
|
||||
@@ -383,6 +387,7 @@ impl MotionWorld<'_, '_, '_> {
|
||||
self.player.health.advance_clock();
|
||||
let previous = self.player.position;
|
||||
self.player_movement_tick(direction, jumping)?;
|
||||
self.update_player_pose()?;
|
||||
// The resident scene accepts the resolved local movement directly.
|
||||
// Vanilla dispatches this separately from travel, on packet acceptance.
|
||||
self.check_movement_statistics(previous.vector_to(self.player.position))?;
|
||||
@@ -390,9 +395,12 @@ impl MotionWorld<'_, '_, '_> {
|
||||
.food_tick(&self.resources.health, self.scene.health_rules)
|
||||
}
|
||||
pub fn controlled_simulation_tick(&mut self, keys: crate::sprint::Keys) -> Result<()> {
|
||||
self.update_local_crouching()?;
|
||||
self.player.sprint_input_tick(keys)?;
|
||||
self.player.shift_key_down=keys.shift;
|
||||
let sneak=if self.player.moving_slowly() {Some(self.player.attribute("minecraft:sneaking_speed")? as f32)}else{None};
|
||||
self.simulation_tick(
|
||||
crate::input::modify_input(keys.direction(), None, None),
|
||||
crate::input::modify_input(keys.direction(), None, sneak),
|
||||
keys.jump,
|
||||
)
|
||||
}
|
||||
@@ -484,7 +492,7 @@ impl MovementStatisticsWorld for MotionWorld<'_, '_, '_> {
|
||||
Ok(self.player.sprinting)
|
||||
}
|
||||
fn crouching(&mut self) -> Result<bool> {
|
||||
Ok(false)
|
||||
Ok(self.player.pose==crate::pose::Pose::Crouching)
|
||||
}
|
||||
fn fall_flying(&mut self) -> Result<bool> {
|
||||
Ok(false)
|
||||
@@ -509,6 +517,13 @@ impl MovementBlockProvider for MotionWorld<'_, '_, '_> {
|
||||
Ok(self.block(p))
|
||||
}
|
||||
}
|
||||
impl EdgeBackoffWorld for MotionWorld<'_, '_, '_> {
|
||||
type Error=Box<dyn std::error::Error>;
|
||||
fn max_up_step(&mut self)->Result<f32> {Ok(self.player.attribute("minecraft:step_height")? as f32)}
|
||||
fn staying_on_ground_surface(&mut self)->Result<bool> {Ok(self.player.shift_key_down)}
|
||||
fn on_ground(&mut self)->Result<bool> {Ok(self.player.ground.on_ground)}
|
||||
fn no_collision(&mut self,bounds:Aabb)->Result<bool> {self.no_player_collision(bounds)}
|
||||
}
|
||||
impl EntityMoveWorld for MotionWorld<'_, '_, '_> {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
fn no_physics(&self) -> bool {
|
||||
@@ -549,8 +564,13 @@ impl EntityMoveWorld for MotionWorld<'_, '_, '_> {
|
||||
self.player.velocity = value;
|
||||
Ok(())
|
||||
}
|
||||
fn back_off_edge(&mut self, movement: Vec3, _: MoverType) -> Result<Vec3> {
|
||||
Ok(movement)
|
||||
fn back_off_edge(&mut self, movement: Vec3, mover: MoverType) -> Result<Vec3> {
|
||||
let input=EdgeBackoffInput {
|
||||
movement,bounds:self.player.bounds(),flying:false,
|
||||
self_or_player:matches!(mover,MoverType::SelfMovement|MoverType::Player),
|
||||
fall_distance:self.player.fall_distance,query_budget:1_000_000,
|
||||
};
|
||||
edge_backoff::back_off_from_edge(self,input).map_err(|e|io::Error::other(e.to_string()).into())
|
||||
}
|
||||
fn collide(&mut self, movement: Vec3) -> Result<Vec3> {
|
||||
let max_step = self.player.attribute("minecraft:step_height")? as f32;
|
||||
@@ -698,7 +718,7 @@ impl EntityMoveWorld for MotionWorld<'_, '_, '_> {
|
||||
impl RestitutionWorld for MotionWorld<'_, '_, '_> {
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
fn suppressing_bounce(&mut self) -> Result<bool> {
|
||||
Ok(false)
|
||||
Ok(self.player.shift_key_down)
|
||||
}
|
||||
fn entity_bounciness(&mut self) -> Result<f64> {
|
||||
self.player.attribute("minecraft:bounciness")
|
||||
@@ -814,7 +834,7 @@ impl LivingAirWorld for MotionWorld<'_, '_, '_> {
|
||||
== "minecraft:scaffolding")
|
||||
}
|
||||
fn suppressing_ladder_slide(&mut self) -> Result<bool> {
|
||||
Ok(false)
|
||||
Ok(self.player.shift_key_down)
|
||||
}
|
||||
fn is_player_class(&self) -> bool {
|
||||
true
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//! Resident player pose, native dimension arithmetic and dry pose-fit selection.
|
||||
//! Full synchronized entity schemas, sleeping and fluid/flight lifecycles remain
|
||||
//! separate from these stored movement fields.
|
||||
use crate::{Result, motion::{MotionWorld, PlayerMotion}};
|
||||
use horizon_core::geometry::{Aabb, Vec3};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum Pose {
|
||||
#[default]
|
||||
Standing,
|
||||
Crouching,
|
||||
Swimming,
|
||||
}
|
||||
impl Pose {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self { Self::Standing=>"STANDING",Self::Crouching=>"CROUCHING",Self::Swimming=>"SWIMMING" }
|
||||
}
|
||||
pub fn dimensions(self, scale: f32) -> Dimensions {
|
||||
let (height,eye_height)=match self {Self::Standing=>(1.8,1.62),Self::Crouching=>(1.5,1.27),Self::Swimming=>(0.6,0.4)};
|
||||
Dimensions { width:0.6*scale,height:height*scale,eye_height:eye_height*scale }
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Dimensions {
|
||||
pub width:f32,
|
||||
pub height:f32,
|
||||
pub eye_height:f32,
|
||||
}
|
||||
impl Dimensions {
|
||||
pub fn bounds(self, position:Vec3)->Aabb {
|
||||
let half=f64::from(self.width/2.0);
|
||||
Aabb::new(Vec3::new(position.x-half,position.y,position.z-half),
|
||||
Vec3::new(position.x+half,position.y+f64::from(self.height),position.z+half))
|
||||
}
|
||||
}
|
||||
impl PlayerMotion {
|
||||
pub fn dimensions_for_pose(&mut self, pose:Pose)->Result<Dimensions> {
|
||||
Ok(pose.dimensions(self.attribute("minecraft:scale")? as f32))
|
||||
}
|
||||
pub fn refresh_dimensions(&mut self)->Result<()> {
|
||||
self.dimensions=self.dimensions_for_pose(self.pose)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_pose(&mut self, pose:Pose)->Result<()> {
|
||||
if self.pose!=pose {
|
||||
self.pose=pose;
|
||||
self.refresh_dimensions()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn moving_slowly(&self)->bool {
|
||||
self.local_crouching || self.pose==Pose::Swimming // dry scene: not in water
|
||||
}
|
||||
}
|
||||
impl MotionWorld<'_, '_, '_> {
|
||||
pub fn can_fit_pose(&mut self, pose:Pose)->Result<bool> {
|
||||
let bounds=self.player.dimensions_for_pose(pose)?.bounds(self.player.position);
|
||||
self.no_player_collision(bounds.deflate(Vec3::new(1.0e-7,1.0e-7,1.0e-7)))
|
||||
}
|
||||
/// LocalPlayer.aiStep computes crouching BEFORE KeyboardInput.tick, using
|
||||
/// the old shift input. This is distinct from Entity's current pose.
|
||||
pub fn update_local_crouching(&mut self)->Result<()> {
|
||||
self.player.local_crouching=self.can_fit_pose(Pose::Crouching)?
|
||||
&& (self.player.shift_key_down || !self.can_fit_pose(Pose::Standing)?);
|
||||
Ok(())
|
||||
}
|
||||
/// Dry Player.updatePlayerPose after movement: crawl when neither standing
|
||||
/// nor crouching fits; leave the current pose if even crawling cannot fit.
|
||||
pub fn update_player_pose(&mut self)->Result<()> {
|
||||
if !self.can_fit_pose(Pose::Swimming)? {return Ok(());}
|
||||
let desired=if self.player.shift_key_down {Pose::Crouching}else{Pose::Standing};
|
||||
let pose=if self.can_fit_pose(desired)? {desired}
|
||||
else if self.can_fit_pose(Pose::Crouching)? {Pose::Crouching}
|
||||
else {Pose::Swimming};
|
||||
self.player.set_pose(pose)
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,11 @@ public final class VanillaSceneMotionOracle {
|
||||
// method; regular resident movement keeps its actual standing/dry state.
|
||||
Integer statisticsFlags;
|
||||
Object sprintController;
|
||||
Pose scenePose=Pose.STANDING;
|
||||
@Override public Pose getPose(){return scenePose;}
|
||||
@Override public void setPose(Pose pose){if(scenePose!=pose){scenePose=pose;super.refreshDimensions();}}
|
||||
public boolean fitPose(Pose pose){return super.canPlayerFitWithinBlocksAndEntitiesWhen(pose);}
|
||||
public void poseTick(){updatePlayerPose();}
|
||||
boolean statFlag(int bit){return (statisticsFlags&(1<<bit))!=0;}
|
||||
SynchedEntityData.DataItem<Float> healthItem,absorptionItem;Level sceneLevel;Tracker tracker;net.minecraft.server.PlayerAdvancements advancements;
|
||||
Actor(){super(null,null,null,null);}
|
||||
@@ -140,7 +145,7 @@ public final class VanillaSceneMotionOracle {
|
||||
@Override public AttributeMap getAttributes(){return attrs;}
|
||||
@Override protected boolean getSharedFlag(int bit){return (flags&(1<<bit))!=0;}
|
||||
@Override protected void setSharedFlag(int bit,boolean v){flags=(byte)(v?flags|(1<<bit):flags&~(1<<bit));}
|
||||
@Override public void setPos(double x,double y,double z){write(position,this,new Vec3(x,y,z));setBoundingBox(EntityDimensions.scalable(.6f,1.8f).makeBoundingBox(position()));}
|
||||
@Override public void setPos(double x,double y,double z){write(position,this,new Vec3(x,y,z));setBoundingBox(EntityDimensions.scalable(getBbWidth(),getBbHeight()).makeBoundingBox(position()));}
|
||||
@Override public BlockPos blockPosition(){return BlockPos.containing(position());}
|
||||
@Override public BlockState getInBlockState(){return level().getBlockState(blockPosition());}
|
||||
@Override public ItemStack getMainHandItem(){return ItemStack.EMPTY;}
|
||||
@@ -156,10 +161,6 @@ public final class VanillaSceneMotionOracle {
|
||||
@Override public boolean isInLiquid(){return false;}
|
||||
@Override public boolean isFallFlying(){return statisticsFlags!=null&&statFlag(8);}
|
||||
@Override public boolean isSpectator(){return false;}
|
||||
@Override public Pose getPose(){return Pose.STANDING;}
|
||||
@Override public boolean isDescending(){return false;}
|
||||
@Override public boolean isSuppressingBounce(){return false;}
|
||||
@Override public boolean isSuppressingSlidingDownLadder(){return false;}
|
||||
@Override public boolean isNoGravity(){return false;}
|
||||
@Override public boolean hasEffect(Holder<MobEffect> effect){return false;}
|
||||
@Override public MobEffectInstance getEffect(Holder<MobEffect> effect){return null;}
|
||||
@@ -226,6 +227,7 @@ public final class VanillaSceneMotionOracle {
|
||||
p.getEntityData().isDirty(),p.healthItem.isDirty(),p.absorptionItem.isDirty(),p.sceneLevel.time);
|
||||
var food=p.getFoodData();out.printf("food %d %08x %d %d %d %s%n",food.getFoodLevel(),Float.floatToIntBits(food.getSaturationLevel()),foodTimer.getInt(food),p.tickCount,p.sceneLevel.difficulty.ordinal(),p.sceneLevel.rules.regenerate);
|
||||
out.println("input "+p.isSprinting()+" "+(p.sprintController==null?"0 false false":p.sprintController.getClass().getMethod("state").invoke(p.sprintController)));
|
||||
out.printf("pose %s %s %s %08x %08x %08x%n",p.getPose(),p.isShiftKeyDown(),p.sprintController!=null&&((Boolean)p.sprintController.getClass().getMethod("crouching").invoke(p.sprintController)),Float.floatToIntBits(p.getBbWidth()),Float.floatToIntBits(p.getBbHeight()),Float.floatToIntBits(p.getEyeHeight()));
|
||||
}
|
||||
@SuppressWarnings("unchecked") public static void main(String[] args)throws Exception{
|
||||
net.minecraft.SharedConstants.tryDetectVersion();net.minecraft.server.Bootstrap.bootStrap();Path dir=Path.of(args[1]);
|
||||
@@ -255,7 +257,8 @@ public final class VanillaSceneMotionOracle {
|
||||
for(var line:(Iterable<String>)lines::iterator){
|
||||
var row=JsonParser.parseString(line).getAsJsonObject();var p=(Actor)allocate.invoke(unsafe,Actor.class);p.configure(level);p.events=new ArrayList<>();level.actor=p;
|
||||
write(field(Entity.class,"type"),p,EntityTypes.PLAYER);
|
||||
write(field(Entity.class,"passengers"),p,com.google.common.collect.ImmutableList.of());write(field(Entity.class,"dimensions"),p,EntityDimensions.scalable(.6f,1.8f));
|
||||
p.scenePose=Pose.STANDING;
|
||||
write(field(Entity.class,"passengers"),p,com.google.common.collect.ImmutableList.of());write(field(Entity.class,"dimensions"),p,p.getDefaultDimensions(Pose.STANDING));write(field(Entity.class,"eyeHeight"),p,1.62f);
|
||||
write(field(Entity.class,"interpolationHandler"),p,InterpolationHandler.NO_OP);
|
||||
var equipment=new EntityEquipment();write(field(LivingEntity.class,"equipment"),p,equipment);
|
||||
write(field(Player.class,"inventory"),p,new Inventory(p,equipment));
|
||||
@@ -274,10 +277,14 @@ public final class VanillaSceneMotionOracle {
|
||||
p.setDeltaMovement(Vec3.ZERO);p.setPos(VanillaSceneCollisionOracle.vector(row.get("position")));
|
||||
for(var value:row.getAsJsonArray("ops")){
|
||||
var op=value.getAsJsonObject();switch(op.get("mode").getAsString()){
|
||||
case "shift"->p.setShiftKeyDown(op.get("value").getAsBoolean());
|
||||
case "pose_tick"->p.poseTick();
|
||||
case "refresh_dimensions"->p.refreshDimensions();
|
||||
case "pose"->p.setPose(Pose.valueOf(op.get("value").getAsString()));
|
||||
case "controlled_tick"->{
|
||||
if(p.sprintController==null)p.sprintController=Class.forName("dev.horizonlab.parity.VanillaSprintInputOracle$Controller").getConstructor(ServerPlayer.class).newInstance(p);
|
||||
int mask=op.get("value").getAsInt();var direction=(net.minecraft.world.phys.Vec2)p.sprintController.getClass().getMethod("step",int.class).invoke(p.sprintController,mask);
|
||||
p.clock();var before=p.position();p.playerMovementTick(new Vec3(direction.x,0,direction.y),(mask&16)!=0);p.checkMovementStatistics(p.getX()-before.x,p.getY()-before.y,p.getZ()-before.z);p.getFoodData().tick(p);
|
||||
p.clock();var before=p.position();p.playerMovementTick(new Vec3(direction.x,0,direction.y),(mask&16)!=0);p.poseTick();p.checkMovementStatistics(p.getX()-before.x,p.getY()-before.y,p.getZ()-before.z);p.getFoodData().tick(p);
|
||||
}
|
||||
case "health"->p.setHealth(single(op.get("value")));
|
||||
case "absorption"->p.setAbsorptionAmount(single(op.get("value")));
|
||||
@@ -290,7 +297,7 @@ public final class VanillaSceneMotionOracle {
|
||||
case "eat"->p.getFoodData().eat(op.get("nutrition").getAsInt(),single(op.get("value")));
|
||||
case "food_tick"->p.getFoodData().tick(p);
|
||||
case "peaceful"->p.peaceful();
|
||||
case "simulation_tick"->{p.clock();var before=p.position();p.playerMovementTick(VanillaSceneCollisionOracle.vector(op.get("value")),op.get("jumping").getAsBoolean());p.checkMovementStatistics(p.getX()-before.x,p.getY()-before.y,p.getZ()-before.z);p.getFoodData().tick(p);}
|
||||
case "simulation_tick"->{p.clock();var before=p.position();p.playerMovementTick(VanillaSceneCollisionOracle.vector(op.get("value")),op.get("jumping").getAsBoolean());p.poseTick();p.checkMovementStatistics(p.getX()-before.x,p.getY()-before.y,p.getZ()-before.z);p.getFoodData().tick(p);}
|
||||
case "movement_statistics"->{var delta=VanillaSceneCollisionOracle.vector(op.get("value"));p.checkMovementStatistics(delta.x,delta.y,delta.z);}
|
||||
case "statistics_context"->{
|
||||
p.statisticsFlags=op.get("flags").getAsInt();p.getAbilities().invulnerable=op.get("invulnerable").getAsBoolean();
|
||||
|
||||
@@ -51,12 +51,17 @@ public final class VanillaSprintInputOracle {
|
||||
@Override public boolean isUsingItem(){return flag(7);}
|
||||
@Override public boolean isFallFlying(){return flag(9);}
|
||||
@Override public boolean isUnderWater(){return flag(10);}
|
||||
@Override public boolean isMovingSlowly(){return flag(11);}
|
||||
@Override public boolean isMovingSlowly(){return controlled==null?flag(11):super.isMovingSlowly();}
|
||||
@Override public Pose getPose(){return controlled==null?Pose.STANDING:controlled.getPose();}
|
||||
@Override public double getAttributeValue(Holder<net.minecraft.world.entity.ai.attributes.Attribute> attribute){return controlled==null?super.getAttributeValue(attribute):controlled.getAttributeValue(attribute);}
|
||||
@Override public boolean isSwimming(){return flag(12);}
|
||||
@Override public boolean isInWater(){return flag(13);}
|
||||
@Override public boolean onGround(){return flag(14);}
|
||||
@Override public boolean isSleeping(){return false;}
|
||||
@Override public boolean canPlayerFitWithinBlocksAndEntitiesWhen(Pose pose){return true;}
|
||||
@Override public boolean canPlayerFitWithinBlocksAndEntitiesWhen(Pose pose){
|
||||
if(controlled==null)return true;
|
||||
try{var method=Player.class.getDeclaredMethod("canPlayerFitWithinBlocksAndEntitiesWhen",Pose.class);method.setAccessible(true);return (Boolean)method.invoke(controlled,pose);}catch(ReflectiveOperationException e){throw new AssertionError(e);}
|
||||
}
|
||||
@Override public float getEffectBlendFactor(Holder<MobEffect> effect,float partial){return 0;}
|
||||
@Override public boolean isEyeInFluid(TagKey<Fluid> tag){return false;}
|
||||
@Override public boolean onClimbable(){return false;}
|
||||
@@ -94,9 +99,11 @@ public final class VanillaSprintInputOracle {
|
||||
p.horizontalCollision=p.controlled.horizontalCollision;p.minorHorizontalCollision=p.controlled.minorHorizontalCollision;
|
||||
p.calls.clear();for(int i=0;i<keys.length;i++)down.setBoolean(keys[i],(mask&(1<<i))!=0);
|
||||
try{p.aiStep();throw new AssertionError("missing sprint boundary");}catch(Boundary expected){}
|
||||
p.controlled.setShiftKeyDown((mask&32)!=0);
|
||||
return (net.minecraft.world.phys.Vec2)modify.invoke(p,p.input.getMoveVector());
|
||||
}
|
||||
public String state(){return p.trigger()+" "+p.input.hasForwardImpulse()+" "+p.input.keyPresses.shift();}
|
||||
public boolean crouching(){return p.isCrouching();}
|
||||
}
|
||||
static void initialize()throws Exception{
|
||||
if(unsafe!=null)return;
|
||||
|
||||
Reference in new issue
Block a user