Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e617820
refactor: split responsibility in the `body` package
Timtaran Mar 10, 2026
913e085
style: format code
Timtaran Mar 10, 2026
d44a30e
fix: add .gitattributes to enforce consistent line endings
Timtaran Mar 25, 2026
e61b094
refactor: no comment (check #5)
Timtaran Mar 25, 2026
f5a228e
refactor: no comment (check #5)
Timtaran Apr 3, 2026
1bd64a8
refactor: no comment (check #5)
Timtaran Apr 4, 2026
070d3e4
refactor: no comment (check #5)
Timtaran Apr 10, 2026
d21c7d6
fix: gitattributes breaks binary files
Timtaran Apr 10, 2026
34fecb2
refactor(player): use constant types to prevent variables from being …
Timtaran Apr 10, 2026
9efdf9b
refactor: rework grabbed bodies, add trigger interaction, add touch k…
Timtaran Apr 12, 2026
13d9c78
feat: add simple maven publishing
Timtaran Apr 14, 2026
74546b2
refactor: Overhaul IGrabbable events
Timtaran Apr 14, 2026
0e18f12
feat: update to latest velthoric commit
Timtaran Apr 15, 2026
a0eda2c
chore: remove redundant class instantiation
Timtaran Apr 15, 2026
6a68cfc
refactor: use const arg in IGrabbable
Timtaran Apr 19, 2026
bf2481a
refactor: introduce GrabPoint, rework grabbing system
Timtaran Apr 22, 2026
95c4dfc
refactor: add common sources to fabric and neoforge sources, add mave…
Timtaran May 2, 2026
110a9ae
refactor: remove commit hash from nightly build
Timtaran May 3, 2026
c4455b4
refactor: move the wireframe rendering code to utilities
Timtaran May 9, 2026
f9dc0b1
chore(deps): update velthoric, use self-hosted maven
Timtaran May 9, 2026
442df27
refactor(util): use vector-quaternion multiplication instead of creat…
Timtaran May 10, 2026
314cb64
refactor(player): rework grabbing implementation
Timtaran May 10, 2026
f0f8bba
fix(player): only pull and attach detached bodies
Timtaran May 10, 2026
6e99d67
refactor(player): get rid of null inside GrabResult, add better pull …
Timtaran May 13, 2026
9c843bb
chore(player): use unused const in grab interaction
Timtaran May 17, 2026
ff6060b
feat(player): allow dynamic bodies to receive forces
Timtaran May 17, 2026
0403bc4
fix(player): configure constraint once
Timtaran May 17, 2026
4469d12
chore(player): don't pass unused variables to methods
Timtaran May 17, 2026
aff273a
chore(deps): update velthoric
Timtaran May 25, 2026
eb46d08
chore(player): sync ghost body with client, update renderer
Timtaran May 25, 2026
cc6c75c
chore(player): increase dynamic body constraint force
Timtaran May 25, 2026
48c0f20
feat(player): protect player bodies from killing by /vxkill
Timtaran May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
30 changes: 21 additions & 9 deletions .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name: Nightly Build

on: push
on:
push:

jobs:
build:
name: Compile and Release
name: Compile and Publish Snapshot
runs-on: ubuntu-latest

steps:
Expand All @@ -24,19 +25,30 @@ jobs:
- name: Set outputs
id: vars
run: |
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "base_version=$(grep '^mod_version=' gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT

- name: Build with Gradle
- name: Build Snapshot Version
id: version
run: |
CURRENT_VERSION=$(grep "^mod_version=" gradle.properties | cut -d'=' -f2)
./gradlew build -Pmod_version="${CURRENT_VERSION}-${{ steps.vars.outputs.sha_short }}"
VERSION="${{ steps.vars.outputs.base_version }}-SNAPSHOT"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Using version: $VERSION"

- name: Build with Gradle
run: ./gradlew build -Pmod_version="${{ steps.version.outputs.version }}"

- name: Publish Snapshot
env:
REPO_URL: ${{ secrets.REPO_URL }}
REPO_USER: ${{ secrets.REPO_USER }}
REPO_TOKEN: ${{ secrets.REPO_TOKEN }}
if: ${{ env.REPO_URL != '' && env.REPO_USER != '' && env.REPO_TOKEN != '' }}
run: ./gradlew publishAllPublicationsToProjectMavenRepository -Pmod_version="${{ steps.version.outputs.version }}"

- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: InteractiveMC-Build-${{ steps.vars.outputs.sha_short }}
path: |
fabric/build/libs/*.jar
neoforge/build/libs/*.jar

# todo add gametest
neoforge/build/libs/*.jar
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ It enhances VR gameplay by adding real collision and interaction systems, making
> [!NOTE]
> Since Velthoric is under active development, you will likely need to use [nightly builds](https://github.com/xI-Mx-Ix/Velthoric/actions/workflows/nightly.yml). Public releases occur roughly once per month and can become outdated, so we use the latest commits instead.

> [!WARNING]
> If you are playing in singleplayer, it is recommended to set `interpolation_delay_nanos` to `0` in the Velthoric client config to prevent input lag.

## Current features
- Physical interactions via Velthoric
- Player head and body collisions
Expand Down
31 changes: 24 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'dev.architectury.loom' version '1.13-SNAPSHOT' apply false
id 'architectury-plugin' version '3.4-SNAPSHOT'
id 'com.gradleup.shadow' version '8.3.6' apply false
id 'maven-publish'
}

architectury {
Expand Down Expand Up @@ -29,7 +30,8 @@ subprojects {
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
maven { url = 'https://cursemaven.com'}
maven { url = 'https://maven.clwn.org/snapshots'}
maven { url = 'https://cursemaven.com' }
maven { url 'https://jitpack.io' }
maven { url = 'https://api.modrinth.com/maven' }
}
Expand All @@ -55,19 +57,34 @@ subprojects {

// Configure Maven publishing.
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = base.archivesName.get()
from components.java
}
}

// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
maven {
name = "projectMaven"

def repoUrl = System.getenv("REPO_URL")
def snapshotsUrl = "${repoUrl}/snapshots"
def releasesUrl = "${repoUrl}/releases"

url = version.toString().endsWith("-SNAPSHOT")
? uri(snapshotsUrl)
: uri(releasesUrl)

credentials {
username = System.getenv("REPO_USER")
password = System.getenv("REPO_TOKEN")
}

authentication {
basic(BasicAuthentication)
}
}

}
}
}
2 changes: 1 addition & 1 deletion common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies {
implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:$rootProject.mixinextras_version"))

// Using my fork to pin version and be able to apply fixes/changes I need
modImplementation "com.github.ttrndev.Velthoric:velthoric-fabric:$rootProject.velthoric_version"
modImplementation "net.xmx.velthoric:velthoric-fabric:$rootProject.velthoric_version"

modApi "maven.modrinth:vivecraft:$rootProject.vivecraft_version-fabric"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* This file is part of InteractiveMC.
* Licensed under LGPL 3.0.
*/
package net.timtaran.interactivemc.body.duck;

import com.github.stephengold.joltjni.*;
import com.github.stephengold.joltjni.enumerate.EMotionType;
import com.github.stephengold.joltjni.readonly.QuatArg;
import com.github.stephengold.joltjni.readonly.RVec3Arg;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.timtaran.interactivemc.body.player.PlayerBodyPart;
import net.timtaran.interactivemc.body.player.interaction.TriggerState;
import net.timtaran.interactivemc.body.type.GrabPoint;
import net.timtaran.interactivemc.body.type.IGrabbable;
import net.xmx.velthoric.core.body.VxBody;
import net.xmx.velthoric.core.body.VxBodyType;
import net.xmx.velthoric.core.body.factory.VxRigidBodyFactory;
import net.xmx.velthoric.core.body.shape.VxBoxShape;
import net.xmx.velthoric.core.physics.VxPhysicsLayers;
import net.xmx.velthoric.core.physics.world.VxPhysicsWorld;
import org.jetbrains.annotations.Nullable;

import java.util.UUID;

/**
* Test body for debugging grab and trigger interactions.
*/
public class TestDuckRigidBody extends VxBody implements IGrabbable {

/**
* Server-side constructor.
*
* @param type the body type
* @param physicsWorld the physics world
* @param id the unique identifier for this body
*/
public TestDuckRigidBody(VxBodyType type, VxPhysicsWorld physicsWorld, UUID id) {
super(type, physicsWorld, id);
}

/**
* Client-side constructor.
*
* @param type the body type
* @param id the unique identifier for this body
*/
@Environment(EnvType.CLIENT)
public TestDuckRigidBody(VxBodyType type, UUID id) {
super(type, id);
}

public static int createJoltBody(VxBody body, VxRigidBodyFactory factory) {
VxBoxShape shape = new VxBoxShape(new Vec3(0.2f, 0.2f, 0.2f));
try (BodyCreationSettings bcs = new BodyCreationSettings()) {
bcs.setMotionType(EMotionType.Dynamic);
bcs.setObjectLayer(VxPhysicsLayers.MOVING);
return factory.create(shape, bcs);
}
}

@Override
public @Nullable GrabPoint getGrabPoint(Player player, PlayerBodyPart bodyPart, RVec3Arg intersectionPoint, QuatArg rotationDifference) {
return new GrabPoint(intersectionPoint, rotationDifference);
}

@Override
public @Nullable GrabPoint getRemoteGrabPoint(Player player, PlayerBodyPart bodyPart, RVec3Arg intersectionPoint) {
return new GrabPoint(intersectionPoint, new Quat());
}

@Override
public void onTriggerStateUpdate(Player player, PlayerBodyPart bodyPart, TriggerState triggerState) {
System.out.println("Trigger state update: " + triggerState);
}

@Override
public void onGrab(Player player, PlayerBodyPart bodyPart, boolean isAttached) {
System.out.println("on grab: " + isAttached);
}

@Override
public boolean onPull(Player player, PlayerBodyPart bodyPart) {
System.out.println("on pull");
return IGrabbable.super.onPull(player, bodyPart);
}

@Override
public void onRelease(Player player, PlayerBodyPart bodyPart, boolean isAttached) {
System.out.println("on release");
}

@Override
public boolean canRelease(Player player, PlayerBodyPart bodyPart) {
System.out.println("can release");
return IGrabbable.super.canRelease(player, bodyPart);
}

@Override
public void onGrabClient(Player player, InteractionHand interactionHand, boolean isAttached) {
System.out.println("on grab client");
}

@Override
public void onReleaseClient(Player player, InteractionHand interactionHand, boolean isAttached) {
System.out.println("on release client");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This file is part of InteractiveMC.
* Licensed under LGPL 3.0.
*/
package net.timtaran.interactivemc.body.duck;

import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.xmx.velthoric.core.body.VxBody;
import net.xmx.velthoric.core.body.client.VxRenderState;
import net.xmx.velthoric.core.body.client.renderer.VxBodyRenderer;
import net.xmx.velthoric.math.VxConversions;

public class TestDuckRigidBodyRenderer extends VxBodyRenderer<VxBody> {
@Override
public void render(VxBody body, PoseStack poseStack, MultiBufferSource bufferSource, float partialTicks, int packedLight, VxRenderState renderState) {
BlockState blockState = Blocks.YELLOW_CONCRETE.defaultBlockState();

float hx = 0.2f;
float hy = 0.2f;
float hz = 0.2f;

float fullWidth = hx * 2.0f;
float fullHeight = hy * 2.0f;
float fullDepth = hz * 2.0f;

poseStack.pushPose();

poseStack.mulPose(VxConversions.toJoml(renderState.transform.getRotation()));

poseStack.translate(-hx, -hy, -hz);
poseStack.scale(fullWidth, fullHeight, fullDepth);

Minecraft.getInstance().getBlockRenderer().renderSingleBlock(
blockState,
poseStack,
bufferSource,
packedLight,
OverlayTexture.NO_OVERLAY
);

poseStack.popPose();
}
}
Loading
Loading