Skip to content

Commit

Permalink
Merge pull request #12 from magneticflux-/develop
Browse files Browse the repository at this point in the history
Cut new release
  • Loading branch information
magneticflux- committed Sep 17, 2019
2 parents efc76c4 + 547eabb commit 88f6007
Show file tree
Hide file tree
Showing 25 changed files with 1,152 additions and 23 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

59 changes: 55 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,70 @@
[![](http://cf.way2muchnoise.eu/packs/short_mumble-link-fabric.svg)](https://minecraft.curseforge.com/projects/mumble-link-fabric)

# fabric-mumblelink-mod
A Fabric mod that connects to the MumbleLink plugin.
A Fabric mod that connects to the Mumble Link plugin.

Current features:
* Positional audio link to Mumble
* Separation between different dimentions

Planned features:
* Automatic login to a server-provided Mumble server
* Separation of team comms / server comms / etc.

Planned features:
* None needed? Please open an issue if you have a use case not covered yet!

---

# For players

1. Install and start Mumble
2. Enable "Link to Game and Transmit Position"
3. Join a Mumble server
4. Join a world!

# For server operators

The plugin is not required on servers, but having it on the server will allow clients to automatically join a Mumble server of your choosing.

## Configuration

### Trivial
Trivial configuration of the server may be done by specifying the host and port. Clients will simply join the root of the Mumble server. For dimension, team, and server separation, see below.

Dimension separation may also be accomplished by _users_ setting the "Y-Axis multiplier for dimension IDs" to a value above 512. This makes users in the Nether and End appear 512 blocks below and above users in the Overworld, respectively. This approach is not recommended for larger servers, but it may work fine for servers with < 5 people.

### Non-trivial
Most of the non-trivial configuration is done through the use of templated strings. The path and the query string of the URL sent to clients to be opened are template strings, so parts of them may be replaced by user- and server-specific information. Below is a table of available template values:

| Template index | Value source | Example values |
| -------------- | -------------------- | -------------- |
| `{0}` | Full dimension ID | "Minecraft Overworld", "Minecraft The Nether", "Minecraft The End", "Simplevoidworld Void", "Ezwastelands Wastelands" |
| `{1}` | Dimension namespace | "Minecraft", "Simplevoidworld", "Ezwastelands" |
| `{2}` | Dimension path | "Overworld", "The Nether", "The End" |
| `{3}` | Team name | "Red Team", "Team 1", "" |

Some example paths:

- `/My Server/{2}`
- This routes users to a root channel `/My Server` and a subchannel that matches their dimension. The server should have channels `/My Server/Overworld`, `/My Server/The Nether`, and `/My Server/The End` to support the vanilla Minecraft dimensions.
- `/PvP Teams/{2}/{3}`
- This routes users to a root channel `/PvP Teams`, a subchannel that matches the dimension, and a sub-subchannel that matches their team name. The server should have channels for all combinations of dimension and team.

If a server does not have a channel for a certain path, the user will remain in their previous channel until their client receives a path that exists.

For more reference on Mumble URLs, see their [wiki page](https://wiki.mumble.info/wiki/Mumble_URL).

# Security considerations

Servers with this mod will be able to open Mumble URLs through your client. Servers _can not_ open arbitrary URLs, because only the required information ([host](https://tools.ietf.org/html/rfc3986#section-3.2.2), [port](https://tools.ietf.org/html/rfc3986#section-3.2.3), [path](https://tools.ietf.org/html/rfc3986#section-3.3), and [query](https://tools.ietf.org/html/rfc3986#section-3.4)) is sent to the client. It is important to note that a full URL is _not_ sent to the client; the [scheme](https://tools.ietf.org/html/rfc3986#section-3.1), [user info](https://tools.ietf.org/html/rfc3986#section-3.2.1), and [fragment identifier](https://tools.ietf.org/html/rfc3986#section-3.5) are hard-coded. Only vulnerabilities in Mumble may be exploited, and only by servers trusted by the player.

The client-side URI construction is this fragment:
```kotlin
val uri = URI("mumble", null, host, port, path, query, null)
```

---

If you feel generous or want to encourage me, you can throw a few dollars my way here:
If you feel generous or want to encourage my work, you can throw a few dollars my way here:

[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/L4L0XZWT)

Expand Down
16 changes: 11 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ plugins {
id 'idea'
id 'maven-publish'
id 'fabric-loom' version '0.2.5-SNAPSHOT'
id 'com.github.ben-manes.versions' version '0.22.0'
id 'com.github.ben-manes.versions' version '0.24.0'
id 'com.github.johnrengelman.shadow' version '5.1.0'
id 'com.matthewprenger.cursegradle' version '1.4.0'
id 'org.jetbrains.kotlin.jvm' version '1.3.41'
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
id 'org.shipkit.java' version '2.2.5'
}

Expand Down Expand Up @@ -50,10 +50,16 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

modCompile 'net.fabricmc:fabric-language-kotlin:1.3.40+build.1'
include 'net.fabricmc:fabric-language-kotlin:1.3.40+build.1'
modCompile 'net.fabricmc:fabric-language-kotlin:1.3.50+build.3'
include 'net.fabricmc:fabric-language-kotlin:1.3.50+build.3'

modCompile 'io.github.prospector:modmenu:1.7.9+build.118'
modCompile 'io.github.prospector:modmenu:1.7.11+build.121'

modCompile 'cloth-config:ClothConfig:0.2.4.17'
include 'cloth-config:ClothConfig:0.2.4.17'

modCompile 'me.sargunvohra.mcmods:auto-config:1.2.0+mc1.14.4'
include 'me.sargunvohra.mcmods:auto-config:1.2.0+mc1.14.4'

shaded('com.skaggsm:java-mumble-link:0.2.4') {
exclude group: 'net.java.dev.jna'
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.14.4
yarn_mappings=1.14.4+build.9
loader_version=0.4.8+build.159
yarn_mappings=1.14.4+build.12
loader_version=0.6.1+build.165
# Mod Properties
maven_group=com.skaggsm
archives_base_name=fabric-mumblelink-mod
curseforge_id=321669
# Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric
fabric_version=0.3.1+build.208
fabric_version=0.3.2+build.226-1.14
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.skaggsm.mumblelinkmod;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.world.dimension.DimensionType;

/**
* Created by Mitchell Skaggs on 5/29/2019.
*/

public interface ServerOnChangeDimensionCallback {
Event<ServerOnChangeDimensionCallback> EVENT = EventFactory.createArrayBacked(ServerOnChangeDimensionCallback.class, (listeners) ->
EventFactory.isProfilingEnabled() ?
(toDimension, player) -> {
player.server.getProfiler().push("fabricServerOnChangeDimension");

for (ServerOnChangeDimensionCallback event : listeners) {
player.server.getProfiler().push(EventFactory.getHandlerName(event));
event.onChangeDimension(toDimension, player);
player.server.getProfiler().pop();
}

player.server.getProfiler().pop();
} :
(toDimension, player) -> {
for (ServerOnChangeDimensionCallback event : listeners) {
event.onChangeDimension(toDimension, player);
}

});

void onChangeDimension(DimensionType toDimension, ServerPlayerEntity player);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.skaggsm.mumblelinkmod;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.network.ServerPlayerEntity;

/**
* Created by Mitchell Skaggs on 5/29/2019.
*/

public interface ServerOnConnectCallback {
Event<ServerOnConnectCallback> EVENT = EventFactory.createArrayBacked(ServerOnConnectCallback.class, (listeners) ->
EventFactory.isProfilingEnabled() ?
(player) -> {
player.server.getProfiler().push("fabricServerOnConnect");

for (ServerOnConnectCallback event : listeners) {
player.server.getProfiler().push(EventFactory.getHandlerName(event));
event.onConnect(player);
player.server.getProfiler().pop();
}

player.server.getProfiler().pop();
} :
(player) -> {
for (ServerOnConnectCallback event : listeners) {
event.onConnect(player);
}

});

void onConnect(ServerPlayerEntity player);
}
34 changes: 34 additions & 0 deletions src/main/java/com/skaggsm/mumblelinkmod/ServerOnTeamsModify.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.skaggsm.mumblelinkmod;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.scoreboard.ServerScoreboard;
import net.minecraft.server.MinecraftServer;

/**
* Created by Mitchell Skaggs on 5/29/2019.
*/

public interface ServerOnTeamsModify {
Event<ServerOnTeamsModify> EVENT = EventFactory.createArrayBacked(ServerOnTeamsModify.class, (listeners) ->
EventFactory.isProfilingEnabled() ?
(scoreboard, server) -> {
server.getProfiler().push("fabricServerOnScoreboardUpdate");

for (ServerOnTeamsModify event : listeners) {
server.getProfiler().push(EventFactory.getHandlerName(event));
event.onScoreboardModify(scoreboard, server);
server.getProfiler().pop();
}

server.getProfiler().pop();
} :
(scoreboard, server) -> {
for (ServerOnTeamsModify event : listeners) {
event.onScoreboardModify(scoreboard, server);
}

});

void onScoreboardModify(ServerScoreboard scoreboard, MinecraftServer server);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.skaggsm.mumblelinkmod.mixin;

import com.skaggsm.mumblelinkmod.ServerOnConnectCallback;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

/**
* Created by Mitchell Skaggs on 5/29/2019.
*/
@Mixin(PlayerManager.class)
public class MixinPlayerManager {
@Inject(method = "onPlayerConnect", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void onPlayerConnect(ClientConnection clientConnection_1, ServerPlayerEntity serverPlayerEntity_1, CallbackInfo ci) {
ServerOnConnectCallback.EVENT.invoker().onConnect(serverPlayerEntity_1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.skaggsm.mumblelinkmod.mixin;

import com.skaggsm.mumblelinkmod.ServerOnChangeDimensionCallback;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.world.dimension.DimensionType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

/**
* Created by Mitchell Skaggs on 9/15/2019.
*/
@Mixin(ServerPlayerEntity.class)
public abstract class MixinServerPlayerEntity {
@Inject(method = "changeDimension", at = @At(value = "RETURN"))
private void onChangeDimension(DimensionType dimensionType_1, CallbackInfoReturnable<Entity> cir) {
ServerOnChangeDimensionCallback.EVENT.invoker().onChangeDimension(dimensionType_1, (ServerPlayerEntity) (Object) this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.skaggsm.mumblelinkmod.mixin;

import com.skaggsm.mumblelinkmod.ServerOnTeamsModify;
import net.minecraft.scoreboard.ServerScoreboard;
import net.minecraft.scoreboard.Team;
import net.minecraft.server.MinecraftServer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

/**
* Created by Mitchell Skaggs on 5/29/2019.
*/
@Mixin(ServerScoreboard.class)
public class MixinServerScoreboard {
@Shadow
@Final
private MinecraftServer server;

@Inject(method = "addPlayerToTeam", at = @At(value = "RETURN", ordinal = 0))
private void onAddPlayerToTeam(String string_1, Team team_1, CallbackInfoReturnable<Boolean> cir) {
doOnScoreboardModify();
}

@Inject(method = "removePlayerFromTeam", at = @At("TAIL"))
private void onRemovePlayerFromTeam(String string_1, Team team_1, CallbackInfo ci) {
doOnScoreboardModify();
}

@Inject(method = "updateRemovedTeam", at = @At("TAIL"))
private void onUpdateRemovedTeam(Team team_1, CallbackInfo ci) {
doOnScoreboardModify();
}

private void doOnScoreboardModify() {
ServerScoreboard scoreboard = (ServerScoreboard) (Object) this;
ServerOnTeamsModify.EVENT.invoker().onScoreboardModify(scoreboard, this.server);
}
}
35 changes: 32 additions & 3 deletions src/main/kotlin/com/skaggsm/mumblelinkmod/ClientMumbleLinkMod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package com.skaggsm.mumblelinkmod

import com.skaggsm.jmumblelink.MumbleLink
import com.skaggsm.jmumblelink.MumbleLinkImpl
import com.skaggsm.mumblelinkmod.config.MumbleLinkConfig.AutoLaunchOption.ACCEPT
import com.skaggsm.mumblelinkmod.config.MumbleLinkConfig.AutoLaunchOption.IGNORE
import com.skaggsm.mumblelinkmod.network.SendMumbleURL
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.event.client.ClientTickCallback
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry
import net.minecraft.util.math.Vec3d
import java.awt.Desktop
import java.net.URI

/**
* Convert to a float 3-array in a left-handed coordinate system.
Expand All @@ -22,6 +28,24 @@ object ClientMumbleLinkMod : ClientModInitializer {
private var mumble: MumbleLink? = null

override fun onInitializeClient() {
ClientSidePacketRegistry.INSTANCE.register(SendMumbleURL.ID) { _, bytes ->
when (MumbleLinkMod.config.config.mumbleAutoLaunchOption) {
ACCEPT -> {
val host = bytes.readString()
val port = bytes.readInt()
val path = bytes.readString().let { if (it == "") null else it }
val query = bytes.readString().let { if (it == "") null else it }

val uri = URI("mumble", null, host, port, path, query, null)

println("Opening $uri")
Desktop.getDesktop().browse(uri)
}
IGNORE -> {
}
}
}

ClientTickCallback.EVENT.register(ClientTickCallback {
if (it.world != null) {
val mumble = ensureLinked()
Expand All @@ -30,8 +54,8 @@ object ClientMumbleLinkMod : ClientModInitializer {
val camDir = it.player.rotationVecClient.toLHArray
val camTop = floatArrayOf(0f, 1f, 0f)

// Makes people in other dimensions far away so they're muted.
val yAxisAdjuster = it.world.dimension.type.rawId * 512f
// Make people in other dimensions far away so they're muted.
val yAxisAdjuster = it.world.dimension.type.rawId * MumbleLinkMod.config.config.mumbleDimensionYAxisAdjust
camPos[1] += yAxisAdjuster

mumble.uiVersion = 2
Expand All @@ -49,7 +73,7 @@ object ClientMumbleLinkMod : ClientModInitializer {

mumble.identity = it.player.uuidAsString

mumble.context = "Minecraft" //"${it.world.dimension.type}-${it.player.scoreboardTeam?.name}"
mumble.context = "Minecraft"

mumble.description = "A Minecraft mod that provides position data to Mumble."
} else {
Expand Down Expand Up @@ -80,4 +104,9 @@ object ClientMumbleLinkMod : ClientModInitializer {
println("Unlinked")
}
}

init {
// Required to open URIs
System.setProperty("java.awt.headless", "false")
}
}
Loading

0 comments on commit 88f6007

Please sign in to comment.