diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2ca3795 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,40 @@ +# Automatically build the project and run any configured tests for every push +# and submitted pull request. This can help catch issues that only occur on +# certain platforms or Java versions, and provides a first line of defence +# against bad commits. + +name: build +on: [pull_request, push] + +jobs: + build: + strategy: + matrix: + # Use these Java versions + java: [ + 17, # Current Java LTS & minimum supported by Minecraft + ] + # and run on both Linux and Windows + os: [ubuntu-22.04, windows-2022] + runs-on: ${{ matrix.os }} + steps: + - name: checkout repository + uses: actions/checkout@v3 + - name: validate gradle wrapper + uses: gradle/wrapper-validation-action@v1 + - name: setup jdk ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'microsoft' + - name: make gradle wrapper executable + if: ${{ runner.os != 'Windows' }} + run: chmod +x ./gradlew + - name: build + run: ./gradlew build + - name: capture build artifacts + if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS + uses: actions/upload-artifact@v3 + with: + name: Artifacts + path: build/libs/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 09cd281..c476faf 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,10 @@ bin/ # fabric run/ + +# java + +hs_err_*.log +replay_*.log +*.hprof +*.jfr diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4671b43 --- /dev/null +++ b/build.gradle @@ -0,0 +1,91 @@ +plugins { + id 'fabric-loom' version '1.4-SNAPSHOT' + id 'maven-publish' + id "org.jetbrains.kotlin.jvm" version "1.9.10" +} + +version = "mc${ project.minecraft_version }-v${ project.mod_version }" +group = project.maven_group + +base { archivesName = project.archives_base_name } + +repositories { + + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // 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://jitpack.io" } + +} + +dependencies { + + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + modImplementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}" + // Uncomment the following line to enable the deprecated Fabric API modules. + // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. + + // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" + + implementation "com.fasterxml.jackson.core:jackson-annotations:2.14.1" + implementation "com.fasterxml.jackson.core:jackson-core:2.14.1" + implementation "com.fasterxml.jackson.core:jackson-databind:2.14.1" + implementation "com.github.sapher:youtubedl-java:1.1" + implementation "net.bramp.ffmpeg:ffmpeg:0.7.0" + implementation "commons-validator:commons-validator:1.7" + implementation "com.squareup.okhttp:okhttp:2.7.5" + implementation "com.googlecode.soundlibs:mp3spi:1.9.5.4" + +} + +processResources { + + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { expand "version": project.version } + +} + +tasks.withType(JavaCompile).configureEach { it.options.release = 17 } + +tasks.withType( org.jetbrains.kotlin.gradle.tasks.KotlinCompile ).all { + kotlinOptions { jvmTarget = 17 } +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +jar { + from("LICENSE") { rename { "${it}_${ project.base.archivesName.get() }" } } +} + +// configure the maven publication +publishing { + + publications { mavenJava(MavenPublication) { 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. + } + +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 8fafc35..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,69 +0,0 @@ - -plugins { - id("fabric-loom") - val kotlinVersion: String by System.getProperties() - kotlin("jvm").version(kotlinVersion) -} - -base { - val archivesBaseName: String by project - archivesName.set(archivesBaseName) -} - -val modVersion: String by project -version = modVersion - -val mavenGroup: String by project -group = mavenGroup - -repositories { - mavenCentral() - maven( url = "https://jitpack.io" ) -} - -dependencies { - val minecraftVersion: String by project - minecraft("com.mojang", "minecraft", minecraftVersion) - val yarnMappings: String by project - mappings("net.fabricmc", "yarn", yarnMappings, null, "v2") - val loaderVersion: String by project - modImplementation("net.fabricmc", "fabric-loader", loaderVersion) - val fabricVersion: String by project - modImplementation("net.fabricmc.fabric-api", "fabric-api", fabricVersion) - val fabricKotlinVersion: String by project - modImplementation("net.fabricmc", "fabric-language-kotlin", fabricKotlinVersion) - include( implementation("com.fasterxml.jackson.core:jackson-annotations:2.14.1")!! ) - include( implementation("com.fasterxml.jackson.core:jackson-core:2.14.1")!! ) - include( implementation("com.fasterxml.jackson.core:jackson-databind:2.14.1")!! ) - include( implementation("com.github.sapher:youtubedl-java:1.1")!! ) - include( implementation("net.bramp.ffmpeg:ffmpeg:0.7.0")!! ) - include( implementation("commons-validator:commons-validator:1.7")!! ) - include( implementation("com.squareup.okhttp:okhttp:2.7.5")!! ) - include( implementation("com.googlecode.soundlibs:mp3spi:1.9.5.4")!! ) -} - -tasks { - val javaVersion = JavaVersion.VERSION_17 - withType { - options.encoding = "UTF-8" - sourceCompatibility = javaVersion.toString() - targetCompatibility = javaVersion.toString() - options.release.set(javaVersion.toString().toInt()) - } - withType { - kotlinOptions { jvmTarget = javaVersion.toString() } - sourceCompatibility = javaVersion.toString() - targetCompatibility = javaVersion.toString() - } - jar { from("LICENSE") { rename { "${it}_${base.archivesName}" } } } - processResources { - inputs.property("version", project.version) - filesMatching("fabric.mod.json") { expand(mutableMapOf("version" to project.version)) } - } - java { - toolchain { languageVersion.set(JavaLanguageVersion.of(javaVersion.toString())) } - sourceCompatibility = javaVersion - targetCompatibility = javaVersion - withSourcesJar() - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 75c0e43..149bfaf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,18 @@ -kotlin.code.style = official -org.gradle.jvmargs = -Xmx1G -org.gradle.warning.mode = all -# Check these on https://fabricmc.net/versions.html -minecraftVersion = 1.19.3 -yarnMappings = 1.19.3+build.5 -loaderVersion = 0.14.22 -# Fabric API -fabricVersion = 0.76.1+1.19.3 -loomVersion = 1.0-SNAPSHOT +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.19.3 +yarn_mappings=1.19.3+build.5 +loader_version=0.14.22 +fabric_kotlin_version=1.10.10+kotlin.1.9.10 + # Mod Properties -modVersion = mc1.19.3-v4.0.0 -mavenGroup = com.enginemachiner -archivesBaseName = honkytones -# Kotlin -systemProp.kotlinVersion = 1.6.21 -fabricKotlinVersion = 1.7.4+kotlin.1.6.21 +mod_version=1.4.0.2 +maven_group=com.enginemachiner.honkytones +archives_base_name=honkytones + +# Dependencies +fabric_version=0.76.1+1.19.3 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92f06b5..ac72c34 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..0adc8e1 100644 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,99 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..93e3f59 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..75c4d72 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + mavenCentral() + gradlePluginPortal() + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 7cab0d8..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -pluginManagement { - repositories { - maven("https://maven.fabricmc.net") { name = "Fabric" } - mavenCentral() - gradlePluginPortal() - } - plugins { - val loomVersion: String by settings - id("fabric-loom").version(loomVersion) - val kotlinVersion: String by System.getProperties() - kotlin("jvm").version(kotlinVersion) - } -} \ No newline at end of file diff --git a/src/main/java/com/enginemachiner/honkytones/mixin/ClientWorldMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/ClientWorldMixin.java new file mode 100644 index 0000000..196505f --- /dev/null +++ b/src/main/java/com/enginemachiner/honkytones/mixin/ClientWorldMixin.java @@ -0,0 +1,18 @@ +package com.enginemachiner.honkytones.mixin; + +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayer; +import net.minecraft.client.world.ClientWorld; +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; + +@Mixin( ClientWorld.class ) +public class ClientWorldMixin { + + @Inject( at = @At("HEAD"), method = "disconnect" ) + private void pauseAllMidiMusicPlayers( CallbackInfo callback ) { + MusicPlayer.Companion.getList().forEach( MusicPlayer::pauseOnMidiHost ); + } + +} diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/CrashReportMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/CrashReportMixin.java similarity index 94% rename from src/main/java/com/enginemachiner/honkytones/mixins/CrashReportMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/CrashReportMixin.java index 66b2b6a..bf44f55 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/CrashReportMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/CrashReportMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins; +package com.enginemachiner.honkytones.mixin; import net.minecraft.util.crash.CrashReport; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/chest/ChestBlockEntityAccessor.java b/src/main/java/com/enginemachiner/honkytones/mixin/chest/ChestBlockEntityAccessor.java similarity index 89% rename from src/main/java/com/enginemachiner/honkytones/mixins/chest/ChestBlockEntityAccessor.java rename to src/main/java/com/enginemachiner/honkytones/mixin/chest/ChestBlockEntityAccessor.java index fd7818d..cbe76a4 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/chest/ChestBlockEntityAccessor.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/chest/ChestBlockEntityAccessor.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.chest; +package com.enginemachiner.honkytones.mixin.chest; import net.minecraft.block.entity.ChestBlockEntity; import net.minecraft.block.entity.ChestLidAnimator; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/chest/LidAnimatorAccessor.java b/src/main/java/com/enginemachiner/honkytones/mixin/chest/LidAnimatorAccessor.java similarity index 87% rename from src/main/java/com/enginemachiner/honkytones/mixins/chest/LidAnimatorAccessor.java rename to src/main/java/com/enginemachiner/honkytones/mixin/chest/LidAnimatorAccessor.java index 4f65c53..23e99fa 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/chest/LidAnimatorAccessor.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/chest/LidAnimatorAccessor.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.chest; +package com.enginemachiner.honkytones.mixin.chest; import net.minecraft.block.entity.ChestLidAnimator; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentHelperMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentHelperMixin.java similarity index 97% rename from src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentHelperMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentHelperMixin.java index 80822ec..98d3e51 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentHelperMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentHelperMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.enchantments; +package com.enginemachiner.honkytones.mixin.enchantments; import com.enginemachiner.honkytones.Verify; import com.enginemachiner.honkytones.items.instruments.Instrument; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentMixin.java similarity index 94% rename from src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentMixin.java index 3d74c2c..750e507 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/enchantments/EnchantmentMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/enchantments/EnchantmentMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.enchantments; +package com.enginemachiner.honkytones.mixin.enchantments; import com.enginemachiner.honkytones.items.instruments.Instrument; import com.google.common.collect.Multimap; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MenuMob.java b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MenuMob.java similarity index 96% rename from src/main/java/com/enginemachiner/honkytones/mixins/mob/MenuMob.java rename to src/main/java/com/enginemachiner/honkytones/mixin/mob/MenuMob.java index 0f120c3..5facbc9 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MenuMob.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MenuMob.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.mob; +package com.enginemachiner.honkytones.mixin.mob; import com.enginemachiner.honkytones.MixinLogic; import net.minecraft.entity.mob.MobEntity; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MobEntityMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MobEntityMixin.java similarity index 96% rename from src/main/java/com/enginemachiner/honkytones/mixins/mob/MobEntityMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/mob/MobEntityMixin.java index 8e9cc13..7bc6050 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MobEntityMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MobEntityMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.mob; +package com.enginemachiner.honkytones.mixin.mob; import com.enginemachiner.honkytones.MixinLogic; import com.enginemachiner.honkytones.NBT; @@ -10,6 +10,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -22,6 +23,7 @@ @Mixin( MobEntity.class ) public class MobEntityMixin { + @Unique private static final Instrument.Companion companion = Instrument.Companion; /** Make mobs play instruments when attacking. */ diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MobsCanPlay.java b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MobsCanPlay.java similarity index 95% rename from src/main/java/com/enginemachiner/honkytones/mixins/mob/MobsCanPlay.java rename to src/main/java/com/enginemachiner/honkytones/mixin/mob/MobsCanPlay.java index 35a2721..b843b92 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/mob/MobsCanPlay.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/mob/MobsCanPlay.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.mob; +package com.enginemachiner.honkytones.mixin.mob; import com.enginemachiner.honkytones.ItemKt; import com.enginemachiner.honkytones.MixinLogic; @@ -16,6 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; // DrownedEntity.class and PillagerEntity.class are ranged mobs, they can't use instruments, big sad. +// TODO: Make them throw projectiles. @Mixin( { MobEntity.class, AbstractSkeletonEntity.class, diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/player/ClientPlayerMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/player/ClientPlayerMixin.java similarity index 93% rename from src/main/java/com/enginemachiner/honkytones/mixins/player/ClientPlayerMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/player/ClientPlayerMixin.java index bf6bda0..68c7cb1 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/player/ClientPlayerMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/player/ClientPlayerMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.player; +package com.enginemachiner.honkytones.mixin.player; import com.enginemachiner.honkytones.items.instruments.Instrument; import net.fabricmc.api.EnvType; @@ -13,7 +13,6 @@ // I learnt that to point to the right method you have to use the bytecode reader. -@Environment(EnvType.CLIENT) @Mixin( ClientPlayerEntity.class ) public class ClientPlayerMixin { diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/player/FloppyOnScreen.java b/src/main/java/com/enginemachiner/honkytones/mixin/player/FloppyOnScreen.java similarity index 88% rename from src/main/java/com/enginemachiner/honkytones/mixins/player/FloppyOnScreen.java rename to src/main/java/com/enginemachiner/honkytones/mixin/player/FloppyOnScreen.java index fd3b8d6..69562f2 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/player/FloppyOnScreen.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/player/FloppyOnScreen.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.player; +package com.enginemachiner.honkytones.mixin.player; import com.enginemachiner.honkytones.items.floppy.FloppyDisk; import net.fabricmc.api.EnvType; @@ -15,8 +15,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Environment(EnvType.CLIENT) -@Mixin( ScreenHandler.class ) +@Environment(EnvType.CLIENT) @Mixin( ScreenHandler.class ) public class FloppyOnScreen { /** If a floppy disk queries the title and the stack changes slots. @@ -27,7 +26,7 @@ private void honkyTonesFloppySlotClick( PlayerEntity player, CallbackInfo info ) { - if ( slotIndex < 0 || !player.world.isClient ) return; + if ( slotIndex < 0 ) return; ScreenHandler handler = (ScreenHandler) (Object) this; DefaultedList slots = handler.slots; diff --git a/src/main/java/com/enginemachiner/honkytones/mixins/player/PlayerEntityMixin.java b/src/main/java/com/enginemachiner/honkytones/mixin/player/PlayerEntityMixin.java similarity index 94% rename from src/main/java/com/enginemachiner/honkytones/mixins/player/PlayerEntityMixin.java rename to src/main/java/com/enginemachiner/honkytones/mixin/player/PlayerEntityMixin.java index 6ed9aa5..604b7a9 100644 --- a/src/main/java/com/enginemachiner/honkytones/mixins/player/PlayerEntityMixin.java +++ b/src/main/java/com/enginemachiner/honkytones/mixin/player/PlayerEntityMixin.java @@ -1,4 +1,4 @@ -package com.enginemachiner.honkytones.mixins.player; +package com.enginemachiner.honkytones.mixin.player; import com.enginemachiner.honkytones.items.floppy.FloppyDisk; import com.enginemachiner.honkytones.items.instruments.Instrument; @@ -8,6 +8,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -16,6 +17,7 @@ @Mixin( PlayerEntity.class ) public class PlayerEntityMixin { + @Unique private static final String method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;"; /** If a floppy disk queries the title and is dropped. diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Commands.kt b/src/main/kotlin/com/enginemachiner/honkytones/Commands.kt index 1ee0a5e..5f8d77d 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Commands.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Commands.kt @@ -16,214 +16,218 @@ import net.minecraft.server.command.CommandManager import net.minecraft.server.command.ServerCommandSource import net.minecraft.util.Language -object Commands : DedicatedServerModInitializer { +class Commands : DedicatedServerModInitializer { override fun onInitializeServer() { server() } - @Environment(EnvType.CLIENT) - fun client() { + companion object { - ClientCommandRegistrationCallback.EVENT.register { + @Environment(EnvType.CLIENT) + fun client() { - dispatcher: CommandDispatcher, - _: CommandRegistryAccess -> + ClientCommandRegistrationCallback.EVENT.register { - val literal1 = ClientCommandManager.literal(MOD_NAME) + dispatcher: CommandDispatcher, + _: CommandRegistryAccess -> - val boolKeys = clientConfigKeys[ Boolean::class ]!! - for ( key in boolKeys ) { + val literal1 = ClientCommandManager.literal(MOD_NAME) - val literal2 = ClientCommandManager.literal(key) - val boolArgument = ClientCommandManager.argument( "bool", BoolArgumentType.bool() ) + val boolKeys = clientConfigKeys[ Boolean::class ]!! + for ( key in boolKeys ) { - val command = literal1.then( literal2.then( boolArgument.executes { + val literal2 = ClientCommandManager.literal(key) + val boolArgument = ClientCommandManager.argument( "bool", BoolArgumentType.bool() ) - val bool = BoolArgumentType.getBool( it, "bool" ) - clientConfig[key] = bool; 0 + val command = literal1.then( literal2.then( boolArgument.executes { - } ) ) + val bool = BoolArgumentType.getBool( it, "bool" ) + clientConfig[key] = bool; 0 - dispatcher.register(command) + } ) ) - } + dispatcher.register(command) + + } - val intKeys = clientConfigKeys[ Int::class ]!! - for ( key in intKeys ) { + val intKeys = clientConfigKeys[ Int::class ]!! + for ( key in intKeys ) { - val literal2 = ClientCommandManager.literal(key) - val intArgument = ClientCommandManager.argument( "int", IntegerArgumentType.integer() ) + val literal2 = ClientCommandManager.literal(key) + val intArgument = ClientCommandManager.argument( "int", IntegerArgumentType.integer() ) - val command = literal1.then( literal2.then( intArgument.executes { + val command = literal1.then( literal2.then( intArgument.executes { - var allow = true - val i = IntegerArgumentType.getInteger( it, "int" ) + var allow = true + val i = IntegerArgumentType.getInteger( it, "int" ) - val error = Translation.get("error.$key") + val error = Translation.get("error.$key") - if ( key == "audio_quality" ) allow = i in 1..10 - if ( key == "max_length" ) allow = i > 0 + if ( key == "audio_quality" ) allow = i in 1..10 + if ( key == "max_length" ) allow = i > 0 - if (allow) clientConfig[key] = i else warnUser(error) + if (allow) clientConfig[key] = i else warnUser(error) - 0 + 0 - } ) ) + } ) ) - dispatcher.register(command) + dispatcher.register(command) - } + } - var literal2 = ClientCommandManager.literal("help") - var literal3 = ClientCommandManager.literal("tips") - var command = literal1.then( literal2.then( literal3.executes { + var literal2 = ClientCommandManager.literal("help") + var literal3 = ClientCommandManager.literal("tips") + var command = literal1.then( literal2.then( literal3.executes { - var s = "" + var s = "" - for ( i in 1..10 ) { + for ( i in 1..10 ) { - val tip = Translation.get("help.tip$i"); s += "\n-$tip \n" + val tip = Translation.get("help.tip$i"); s += "\n-$tip \n" - } + } - warnUser(s); 0 + warnUser(s); 0 + + } ) ) - } ) ) + dispatcher.register(command) - dispatcher.register(command) + literal2 = ClientCommandManager.literal("help") + literal3 = ClientCommandManager.literal("commands") + command = literal1.then( literal2.then( literal3.executes { - literal2 = ClientCommandManager.literal("help") - literal3 = ClientCommandManager.literal("commands") - command = literal1.then( literal2.then( literal3.executes { + val key = "restore_defaults"; val message = Translation.get("help.$key") - val key = "restore_defaults"; val message = Translation.get("help.$key") + var s = '\n' + "§6" + key + "§f - " + message + '\n' - var s = '\n' + "§6" + key + "§f - " + message + '\n' + clientConfig.keys.forEach { - clientConfig.keys.forEach { + val key = "help.$it" - val key = "help.$it" + if ( !Translation.has(key) ) return@forEach - if ( !Translation.has(key) ) return@forEach + val message = Translation.get(key) - val message = Translation.get(key) + s += '\n' + "§6" + it + "§f - " + message + '\n' - s += '\n' + "§6" + it + "§f - " + message + '\n' + } - } + warnUser(s); 0 - warnUser(s); 0 + } ) ) - } ) ) + dispatcher.register(command) - dispatcher.register(command) + literal2 = ClientCommandManager.literal("restoreDefaults") + command = literal1.then( literal2.executes { - literal2 = ClientCommandManager.literal("restoreDefaults") - command = literal1.then( literal2.executes { + clientConfigFile.setDefaultProperties(); clientConfig.clear() - clientConfigFile.setDefaultProperties(); clientConfig.clear() + readClientConfig(); val s = Translation.get("message.config_restore") - readClientConfig(); val s = Translation.get("message.config_restore") + warnUser(s); 0 - warnUser(s); 0 + } ) - } ) + dispatcher.register(command) - dispatcher.register(command) + } } - } + private fun server() { - private fun server() { + CommandRegistrationCallback.EVENT.register { - CommandRegistrationCallback.EVENT.register { + dispatcher: CommandDispatcher, + _: CommandRegistryAccess, _: CommandManager.RegistrationEnvironment -> - dispatcher: CommandDispatcher, - _: CommandRegistryAccess, _: CommandManager.RegistrationEnvironment -> + val literal1 = CommandManager.literal(MOD_NAME) - val literal1 = CommandManager.literal(MOD_NAME) + val boolKeys = serverConfigKeys[ Boolean::class ]!! + for ( key in boolKeys ) { - val boolKeys = serverConfigKeys[ Boolean::class ]!! - for ( key in boolKeys ) { + val literal2 = CommandManager.literal(key) + val boolArgument = CommandManager.argument( "bool", BoolArgumentType.bool() ) - val literal2 = CommandManager.literal(key) - val boolArgument = CommandManager.argument( "bool", BoolArgumentType.bool() ) + val command = literal1.then( literal2.then( boolArgument.executes { - val command = literal1.then( literal2.then( boolArgument.executes { + val b = BoolArgumentType.getBool( it, "bool" ) + serverConfig[key] = b; 0 - val b = BoolArgumentType.getBool( it, "bool" ) - serverConfig[key] = b; 0 + } ) ) - } ) ) + dispatcher.register(command) - dispatcher.register(command) + } - } + val intKeys = serverConfigKeys[ Int::class ]!! + for ( key in intKeys ) { - val intKeys = serverConfigKeys[ Int::class ]!! - for ( key in intKeys ) { + val min = serverConfig[key] as Int + val literal2 = CommandManager.literal(key) + val intArgument = CommandManager.argument( "int", IntegerArgumentType.integer() ) + val command = literal1.then( literal2.then( intArgument.executes { - val min = serverConfig[key] as Int - val literal2 = CommandManager.literal(key) - val intArgument = CommandManager.argument( "int", IntegerArgumentType.integer() ) - val command = literal1.then( literal2.then( intArgument.executes { + var allow = true + val i = IntegerArgumentType.getInteger( it, "int" ) - var allow = true - val i = IntegerArgumentType.getInteger( it, "int" ) + val error = Translation.get("error.$key") - val error = Translation.get("error.$key") + if ( key == "mobs_playing_delay" ) allow = i >= min - if ( key == "mobs_playing_delay" ) allow = i >= min + if (allow) serverConfig[key] = i + else warnPlayer( it.source.player!!, error ) - if (allow) serverConfig[key] = i - else warnPlayer( it.source.player!!, error ) + 0 - 0 + } ) ) - } ) ) + dispatcher.register(command) - dispatcher.register(command) + } - } + var literal2 = CommandManager.literal("help") + val literal3 = CommandManager.literal("commands") + var command = literal1.then( literal2.then( literal3.executes { - var literal2 = CommandManager.literal("help") - val literal3 = CommandManager.literal("commands") - var command = literal1.then( literal2.then( literal3.executes { + val key = "restore_defaults"; val message = Translation.get("help.$key") - val key = "restore_defaults"; val message = Translation.get("help.$key") + var s = '\n' + "§6" + it + "§f - " + message + '\n' - var s = '\n' + "§6" + it + "§f - " + message + '\n' + serverConfig.keys.forEach { - serverConfig.keys.forEach { + val key = "help.$it" - val key = "help.$it" + if ( !Language.getInstance().hasTranslation(key) ) return@forEach - if ( !Language.getInstance().hasTranslation(key) ) return@forEach + val message = Translation.get(key) - val message = Translation.get(key) + s += '\n' + "§6" + it + "§f - " + message + '\n' - s += '\n' + "§6" + it + "§f - " + message + '\n' + } - } + warnPlayer( it.source.player!!, s ); 0 - warnPlayer( it.source.player!!, s ); 0 + } ) ) - } ) ) + dispatcher.register(command) - dispatcher.register(command) + literal2 = CommandManager.literal("restoreDefaults") + command = literal1.then( literal2.executes { - literal2 = CommandManager.literal("restoreDefaults") - command = literal1.then( literal2.executes { + serverConfigFile.setDefaultProperties(); serverConfig.clear() - serverConfigFile.setDefaultProperties(); serverConfig.clear() + readServerConfig(); val s = Translation.get("message.config_restore") - readServerConfig(); val s = Translation.get("message.config_restore") + warnPlayer( it.source.player!!, s ); 0 - warnPlayer( it.source.player!!, s ); 0 + } ) - } ) + dispatcher.register(command) - dispatcher.register(command) + } } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Config.kt b/src/main/kotlin/com/enginemachiner/honkytones/Config.kt index be28823..5ec570a 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Config.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Config.kt @@ -25,7 +25,7 @@ val clientConfigKeys = mutableMapOf( Boolean::class to listOf( "keep_downloads", "keep_videos", "mob_particles", "write_device_info", - "player_particles", "sync_all", "music_particles" + "player_particles", "listen_all", "music_particles" ), Int::class to listOf( "audio_quality", "max_length" ), @@ -169,7 +169,7 @@ class ClientConfigFile(path: String) : ConfigFile(path) { companion object { private val default = mapOf( - "sync_all" to "false", "music_particles" to "true", + "listen_all" to "false", "music_particles" to "true", "ffmpeg_directory" to "", "youtube-dl_path" to "youtube-dl", "mob_particles" to "true", "write_device_info" to "true", "player_particles" to "true", "keep_downloads" to "false", diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Entity.kt b/src/main/kotlin/com/enginemachiner/honkytones/Entity.kt index b387e2e..682b41b 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Entity.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Entity.kt @@ -1,5 +1,7 @@ package com.enginemachiner.honkytones +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerBlockEntity +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerEntity import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.block.BlockWithEntity @@ -81,10 +83,24 @@ interface CanBeMuted { val blacklist = mutableMapOf() - fun isMuted(entity: Entity): Boolean { return blacklist.contains(entity) } + fun isMuted(entity: Entity): Boolean { + + if ( entity is MusicPlayerEntity ) { + + val musicPlayer = world()!!.getBlockEntity( entity.blockPos ) + + if ( musicPlayer !is MusicPlayerBlockEntity ) return true + + return !musicPlayer.isListening + + } + + return blacklist.contains(entity) + + } } } -abstract class BlockWithEntity( settings: Settings? ) : BlockWithEntity(settings), ModID \ No newline at end of file +abstract class BlockWithEntity(settings: Settings) : BlockWithEntity(settings), ModID \ No newline at end of file diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Init.kt b/src/main/kotlin/com/enginemachiner/honkytones/Init.kt index 8dc7ad6..9ab5611 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Init.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Init.kt @@ -36,6 +36,7 @@ import net.minecraft.registry.Registry import net.minecraft.sound.SoundEvent import kotlin.reflect.full.createInstance +// TODO: Check shadow / inheritance instead of double casting for mixin objects. // TODO: Check inherited methods on vanilla classes. // TODO: Add advancements. @@ -90,17 +91,15 @@ class Init : ModInitializer, ClientModInitializer { } - fun registerBlock( block: Block, itemSettings: Item.Settings ): Block? { + fun registerBlock( block: Block, itemSettings: Item.Settings ): Block { val s = ( block as ModID ).className().replace( "_block", "" ) val id = modID(s); val block = Registry.register( Registries.BLOCK, id, block ) - val item = BlockItem( block, itemSettings ) + val item = BlockItem( block, itemSettings ); Registry.register( Registries.ITEM, id, item ) - ItemGroupEvents.modifyEntriesEvent(itemGroup).register { it.add(item) } - - Registry.register( Registries.ITEM, id, item ); return block + return block } @@ -114,7 +113,7 @@ class Init : ModInitializer, ClientModInitializer { } - private fun registerSound(path: String): SoundEvent? { + private fun registerSound(path: String): SoundEvent { val id = modID(path); val event = SoundEvent.of(id) @@ -148,7 +147,6 @@ class Init : ModInitializer, ClientModInitializer { private fun register() { // Item group first. - registerItem(ItemGroup) registerItem( FloppyDisk() ); registerItem( DigitalConsole() ) @@ -169,7 +167,7 @@ class Init : ModInitializer, ClientModInitializer { Fuel.register(); NoteProjectileEntity.register() - for ( i in 1..9 ) hitSounds.add( registerSound("hit$i")!! ) + for ( i in 1..9 ) hitSounds.add( registerSound("hit$i") ) registerSound("magic.c3-e3_") diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Input.kt b/src/main/kotlin/com/enginemachiner/honkytones/Input.kt index 2d825d4..08ba66e 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Input.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Input.kt @@ -72,7 +72,7 @@ abstract class GenericReceiver : Receiver { var entity: Entity? = null; var instruments = mutableListOf() - override fun send( message: MidiMessage?, timeStamp: Long ) { + override fun send( message: MidiMessage, timeStamp: Long ) { if ( message !is ShortMessage ) return; client().send { onSend(message) } @@ -84,10 +84,12 @@ abstract class GenericReceiver : Receiver { open fun onPlay( sound: InstrumentSound, stack: ItemStack, entity: Entity ) { - if ( stack.holder != entity ) stack.holder = entity; sound.play(stack) + checkHolder( stack, entity ); sound.play(stack) } + fun checkHolder( stack: ItemStack, entity: Entity ) { if ( stack.holder != entity ) stack.holder = entity } + private fun onSend(message: ShortMessage) { setData(); if ( client().isPaused ) return diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Inventory.kt b/src/main/kotlin/com/enginemachiner/honkytones/Inventory.kt index dff72e1..c7904a3 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Inventory.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Inventory.kt @@ -13,7 +13,7 @@ interface CustomInventory : SidedInventory { fun items(): DefaultedList - override fun getAvailableSlots( side: Direction? ): IntArray { + override fun getAvailableSlots(side: Direction): IntArray { val result = IntArray( items().size ); for ( i in result.indices ) result[i] = i @@ -21,9 +21,9 @@ interface CustomInventory : SidedInventory { } - override fun canInsert( slot: Int, stack: ItemStack?, direction: Direction? ): Boolean { return true } + override fun canInsert( slot: Int, stack: ItemStack, direction: Direction? ): Boolean { return true } - override fun canExtract( slot: Int, stack: ItemStack?, direction: Direction? ): Boolean { return true } + override fun canExtract( slot: Int, stack: ItemStack, direction: Direction ): Boolean { return true } override fun size(): Int { return items().size } @@ -50,9 +50,9 @@ interface CustomInventory : SidedInventory { return Inventories.removeStack( items(), slot ) } - override fun setStack( slot: Int, stack: ItemStack? ) { + override fun setStack( slot: Int, stack: ItemStack ) { - items()[slot] = stack!! + items()[slot] = stack if ( stack.count > maxCountPerStack ) stack.count = maxCountPerStack @@ -60,7 +60,7 @@ interface CustomInventory : SidedInventory { override fun clear() { items().clear() }; override fun markDirty() {} - override fun canPlayerUse( player: PlayerEntity? ): Boolean { return true } + override fun canPlayerUse(player: PlayerEntity): Boolean { return true } } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Item.kt b/src/main/kotlin/com/enginemachiner/honkytones/Item.kt index 5988562..290401f 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Item.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Item.kt @@ -27,11 +27,11 @@ val hands = arrayOf( Hand.MAIN_HAND, Hand.OFF_HAND ) private val equipment = arrayOf( EquipmentSlot.MAINHAND, EquipmentSlot.OFFHAND ) -fun breakEquipment( entity: LivingEntity?, stack: ItemStack ) { +fun breakEquipment( entity: LivingEntity, stack: ItemStack ) { val index = NBT.get(stack).getInt("Hand") - entity!!.sendEquipmentBreakStatus( equipment[index] ) + entity.sendEquipmentBreakStatus( equipment[index] ) } @@ -63,7 +63,11 @@ object ItemGroup: Item( Settings() ), ModID { } -fun defaultSettings(): Item.Settings { return Item.Settings().maxCount(1) } +fun defaultSettings(): Item.Settings { + + return Item.Settings().maxCount(1) + +} interface StackMenu { @@ -79,11 +83,11 @@ interface StackMenu { private interface Trackable { - fun tick( stack: ItemStack?, world: World?, entity: Entity?, slot: Int ) { + fun tick( stack: ItemStack, world: World, entity: Entity, slot: Int ) { - stack!!.holder = entity + stack.holder = entity - if ( world!!.isClient ) return; trackTick( stack, slot ) + if ( world.isClient ) return; trackTick( stack, slot ) if ( !NBT.has(stack) ) setupNBT(stack); val nbt = NBT.get(stack) @@ -110,11 +114,11 @@ private interface Trackable { abstract class ToolItem( material: ToolMaterial, settings: Settings ) : ToolItem( material, settings ), Trackable, ModID { override fun allowNbtUpdateAnimation( - player: PlayerEntity?, hand: Hand?, oldStack: ItemStack?, newStack: ItemStack? + player: PlayerEntity, hand: Hand, oldStack: ItemStack, newStack: ItemStack ): Boolean { return false } override fun inventoryTick( - stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean + stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { tick( stack, world, entity, slot ) } } @@ -122,21 +126,17 @@ abstract class ToolItem( material: ToolMaterial, settings: Settings ) : ToolItem abstract class Item(settings: Settings) : Item(settings), Trackable, ModID { override fun allowNbtUpdateAnimation( - player: PlayerEntity?, hand: Hand?, oldStack: ItemStack?, newStack: ItemStack? + player: PlayerEntity, hand: Hand, oldStack: ItemStack, newStack: ItemStack ): Boolean { return false } override fun inventoryTick( - stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean + stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { tick( stack, world, entity, slot ) } } object Fuel { - private val fuels = mapOf( - AcousticGuitar::class to 2200, - ) - private fun register( kclass: KClass<*>, time: Int ) { FuelRegistry.INSTANCE.add( modItem(kclass), time ) } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/LibImpl.kt b/src/main/kotlin/com/enginemachiner/honkytones/LibImpl.kt index b5d2372..885ef6f 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/LibImpl.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/LibImpl.kt @@ -63,6 +63,7 @@ open class YTDLRequest(input: String) : YoutubeDLRequest(input) { public override fun buildOptions(): String { return super.buildOptions() } } +@Environment(EnvType.CLIENT) private val mapper = ObjectMapper() @Environment(EnvType.CLIENT) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/MusicTheory.kt b/src/main/kotlin/com/enginemachiner/honkytones/MusicTheory.kt index c1d5733..4ffc627 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/MusicTheory.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/MusicTheory.kt @@ -97,11 +97,11 @@ object MusicTheory { } /** Parse and build according to template and range. **/ - private fun builder( template: Set?, range: Set ): Set{ + private fun builder( template: Set, range: Set ): Set{ val output = mutableSetOf() - for ( n in range ) { for ( t in template!! ) { + for ( n in range ) { for ( t in template ) { var s: String diff --git a/src/main/kotlin/com/enginemachiner/honkytones/NBT.kt b/src/main/kotlin/com/enginemachiner/honkytones/NBT.kt index e570c5c..1c53657 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/NBT.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/NBT.kt @@ -49,7 +49,7 @@ object NBT { val holder = stack.holder; if ( holder !is PlayerEntity ) return - putInt( get(stack), "PlayerID", holder.id ) + putInt( get(stack), "PlayerID", holder.id ) // TODO: Consider using UUID as string. } @@ -113,6 +113,8 @@ object NBT { @Environment(EnvType.CLIENT) fun networkNBT(nbt: NbtCompound) { + if ( !canNetwork() ) return + val buf = PacketByteBufs.create(); buf.writeNbt(nbt) ClientPlayNetworking.send( networkID, buf ) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Particles.kt b/src/main/kotlin/com/enginemachiner/honkytones/Particles.kt index 1b9d06c..9399f9c 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Particles.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Particles.kt @@ -126,7 +126,7 @@ open class FollowingParticle( clientWorld: ClientWorld, x: Double, y: Double, z: override fun tick() { - entity ?: return; val entity = entity!! + val entity = entity ?: return if ( entity.isRemoved ) { markDead(); return }; followEntity() @@ -185,12 +185,12 @@ open class BaseParticle( clientWorld: ClientWorld, x: Double, y: Double, z: Doub } override fun createParticle( - parameters: DefaultParticleType?, world: ClientWorld?, + parameters: DefaultParticleType, world: ClientWorld, x: Double, y: Double, z: Double, velocityX: Double, velocityY: Double, velocityZ: Double ): Particle { - val particle = template( world!!, x, y, z ); particle.setSprite(spriteProvider) + val particle = template( world, x, y, z ); particle.setSprite(spriteProvider) return particle diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Projectiles.kt b/src/main/kotlin/com/enginemachiner/honkytones/Projectiles.kt index bfd1b09..a3f2643 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Projectiles.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Projectiles.kt @@ -97,9 +97,9 @@ class NoteProjectileEntity : PersistentProjectileEntity { } - override fun onEntityHit( entityHitResult: EntityHitResult? ) { + override fun onEntityHit(entityHitResult: EntityHitResult) { - val stack = stack ?: return; val entity = entityHitResult!!.entity + val stack = stack ?: return; val entity = entityHitResult.entity if ( this.owner == entity || entity !is LivingEntity ) return @@ -115,7 +115,7 @@ class NoteProjectileEntity : PersistentProjectileEntity { } - override fun onBlockHit( blockHitResult: BlockHitResult? ) { discard() } + override fun onBlockHit(blockHitResult: BlockHitResult) { discard() } override fun getSoundCategory(): SoundCategory { return SoundCategory.PLAYERS } @@ -226,26 +226,26 @@ class NoteProjectileEntity : PersistentProjectileEntity { @Environment(EnvType.CLIENT) class Renderer( context: EntityRendererFactory.Context ) : EntityRenderer(context) { - override fun getTexture( entity: NoteProjectileEntity? ): Identifier { + override fun getTexture( entity: NoteProjectileEntity ): Identifier { return textureID("particle/note/projectile.png") } override fun render( - entity: NoteProjectileEntity?, yaw: Float, tickDelta: Float, - matrices: MatrixStack?, vertexConsumers: VertexConsumerProvider?, + entity: NoteProjectileEntity, yaw: Float, tickDelta: Float, + matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int ) { - matrices!!.push() + matrices.push() - val light = WorldRenderer.getLightmapCoordinates( entity!!.world, entity.blockPos ) + val light = WorldRenderer.getLightmapCoordinates( entity.world, entity.blockPos ) val entry = matrices.peek() val posMatrix: Matrix4f = entry.positionMatrix val normalMatrix: Matrix3f = entry.normalMatrix val layer = RenderLayer.getEntityTranslucent( getTexture(entity) ) - val consumer = vertexConsumers!!.getBuffer(layer) + val consumer = vertexConsumers.getBuffer(layer) val rotation = dispatcher.rotation matrices.scale( SCALE, SCALE, SCALE ) @@ -294,4 +294,4 @@ class NoteProjectileEntity : PersistentProjectileEntity { } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/Screen.kt index d4a07db..e2e3957 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Screen.kt @@ -32,6 +32,8 @@ abstract class StrictSlotScreen( type: ScreenHandlerType<*>, syncID: Int ) : Scr } +// TODO: Check and re-do screen widgets. Specially createButton(). + @Environment(EnvType.CLIENT) class MidiChannelField( textRenderer: TextRenderer, x: Int, y: Int, w: Int, h: Int ) : TextFieldWidget( textRenderer, x, y, w, h, Text.of("Midi Channel Field") diff --git a/src/main/kotlin/com/enginemachiner/honkytones/Utility.kt b/src/main/kotlin/com/enginemachiner/honkytones/Utility.kt index 25853dd..30c9b71 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/Utility.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/Utility.kt @@ -8,9 +8,8 @@ import net.minecraft.text.Text import net.minecraft.util.Identifier import net.minecraft.util.Language import org.apache.commons.validator.routines.UrlValidator -import org.apache.logging.log4j.LogManager -import org.apache.logging.log4j.Logger import org.joml.Vector3f +import org.slf4j.LoggerFactory import kotlin.math.PI import kotlin.reflect.KClass @@ -24,7 +23,7 @@ object Utility { /** Verify logic or methods if they are based on vanilla behaviour, etc. */ annotation class Verify( val reason: String ) -private val logger: Logger = LogManager.getLogger("HonkyTones") +private val logger = LoggerFactory.getLogger("HonkyTones") fun modPrint( any: Any? ) { logger.info("$any") } @@ -131,15 +130,15 @@ private fun threadID(): Long { return Thread.currentThread().id } class Timer( private val tickLimit: Int, private val function: () -> Unit ) { - private val id = threadID() + private val id = threadID(); var remove = false private var ticks = 0; init { timers.add(this) } - private fun kill() { timers.remove(this) } + private fun kill() { remove = true } fun tick() { - if ( id != threadID() ) { return } + if ( id != threadID() || remove ) { return } if ( ticks > tickLimit ) { function(); kill() } else ticks++ @@ -149,7 +148,13 @@ class Timer( private val tickLimit: Int, private val function: () -> Unit ) val timers = mutableListOf() - fun tickTimers() { timers.filterNotNull().forEach { it.tick() } } + fun tickTimers() { + + val list = timers.filterNotNull(); list.forEach { it.tick() } + + list.forEach { if ( it.remove ) timers.remove(it) } + + } } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/MusicPlayerBlock.kt b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/MusicPlayerBlock.kt index 74704f4..c6b2ae0 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/MusicPlayerBlock.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/MusicPlayerBlock.kt @@ -1,6 +1,7 @@ package com.enginemachiner.honkytones.blocks.musicplayer import com.enginemachiner.honkytones.* +import com.enginemachiner.honkytones.BlockWithEntity import com.enginemachiner.honkytones.CanBeMuted.Companion.isMuted import com.enginemachiner.honkytones.Init.Companion.directories import com.enginemachiner.honkytones.Init.Companion.registerBlock @@ -10,6 +11,7 @@ import com.enginemachiner.honkytones.Particles.Companion.WAVE3 import com.enginemachiner.honkytones.Particles.Companion.WAVE4 import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerBlock.Companion.FACING import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerBlock.Companion.PLAYING +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerBlockEntity.Companion.INVENTORY_SIZE import com.enginemachiner.honkytones.items.floppy.FloppyDisk import com.enginemachiner.honkytones.items.instruments.Instrument import com.enginemachiner.honkytones.sound.ExternalSound @@ -29,10 +31,7 @@ import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder import net.fabricmc.fabric.api.`object`.builder.v1.entity.FabricEntityTypeBuilder import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory -import net.minecraft.block.Block -import net.minecraft.block.BlockRenderType -import net.minecraft.block.BlockState -import net.minecraft.block.Material +import net.minecraft.block.* import net.minecraft.block.entity.BlockEntity import net.minecraft.block.entity.BlockEntityTicker import net.minecraft.block.entity.BlockEntityType @@ -77,6 +76,7 @@ import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import net.minecraft.world.World +import net.minecraft.world.explosion.Explosion import java.net.URL import javax.sound.midi.MidiSystem import javax.sound.midi.Sequencer @@ -84,86 +84,98 @@ import kotlin.math.cos import kotlin.math.sin import kotlin.random.Random -// TODO: Make parrots dance. -class MusicPlayerBlock(settings: Settings) : BlockWithEntity(settings), CanBeMuted { +private val coroutine = CoroutineScope( Dispatchers.IO ) + +private fun lookupPlayer( world: World, floppy: ItemStack ): PlayerEntity? { + + val players = world.players; if ( !NBT.has(floppy) ) return null + + val nbt = NBT.get(floppy); return players.find { it.id == nbt.getInt("PlayerID") } - init { defaultState = defaultState.with( PLAYING, false ) } +} + +// TODO: Make parrots dance. +// TODO: Save client listening states (nbt?) so users are not forced to change it themselves. +// TODO: Make a remote class to change the music player settings. (rate, volume, listen) +class MusicPlayerBlock(settings: Settings) : BlockWithEntity(settings) { @Deprecated( "Deprecated in Java", ReplaceWith( "BlockRenderType.MODEL", "net.minecraft.block.BlockRenderType" ) ) - override fun getRenderType( state: BlockState? ): BlockRenderType { return BlockRenderType.MODEL } + override fun getRenderType(state: BlockState): BlockRenderType { return BlockRenderType.MODEL } - override fun createBlockEntity( pos: BlockPos?, state: BlockState? ): BlockEntity { - return MusicPlayerBlockEntity( pos!!, state!! ) + override fun createBlockEntity( pos: BlockPos, state: BlockState ): BlockEntity { + return MusicPlayerBlockEntity( pos, state ) } - override fun appendProperties( builder: StateManager.Builder? ) { - builder!!.add( *arrayOf( FACING, PLAYING ) ) + override fun appendProperties( builder: StateManager.Builder ) { + builder.add( *arrayOf( FACING, PLAYING ) ) } - override fun getPlacementState( context: ItemPlacementContext? ): BlockState? { - val direction = context!!.playerFacing.opposite; return defaultState!!.with( FACING, direction ) + override fun getPlacementState(context: ItemPlacementContext): BlockState { + + val direction = context.playerFacing.opposite + + return defaultState.with( FACING, direction ).with( PLAYING, false ) + } @Deprecated("Deprecated in Java") override fun onUse( - state: BlockState?, world: World?, pos: BlockPos?, - player: PlayerEntity?, hand: Hand?, hit: BlockHitResult? + state: BlockState, world: World, pos: BlockPos, + player: PlayerEntity, hand: Hand, hit: BlockHitResult ): ActionResult { - val player = player!!; val action = ActionResult.CONSUME + val action = ActionResult.CONSUME - val musicPlayer = world!!.getBlockEntity(pos) as MusicPlayerBlockEntity - - val entity = musicPlayer.entity!! - - val mute = mute( player, entity ); if (mute) return action + val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity player.openHandledScreen(musicPlayer); return action } - override fun onBreak( world: World?, pos: BlockPos?, state: BlockState?, player: PlayerEntity? ) { + override fun onBreak( world: World, pos: BlockPos, state: BlockState, player: PlayerEntity ) { - val musicPlayer = world!!.getBlockEntity(pos) as MusicPlayerBlockEntity + val blockEntity = world.getBlockEntity(pos) as MusicPlayerBlockEntity - val isPlaying = musicPlayer.isPlaying; val entity = musicPlayer.entity!! + val isPlaying = blockEntity.isPlaying(); val entity = blockEntity.entity!! - super.onBreak( world, pos, state, player ) + val drop = !isPlaying || player.isCreative - val drop = !isPlaying || player!!.isCreative - if (drop) drop( world, pos, musicPlayer ) else explode(entity) + if (drop) drop( world, pos, blockEntity ) else explode(entity) - entity.remove( Entity.RemovalReason.DISCARDED ) + entity.remove( Entity.RemovalReason.DISCARDED ); super.onBreak( world, pos, state, player ) } @Deprecated("Deprecated in Java") override fun neighborUpdate( - state: BlockState?, world: World?, pos: BlockPos?, - block: Block?, fromPos: BlockPos?, notify: Boolean + state: BlockState, world: World, pos: BlockPos, + block: Block, fromPos: BlockPos, notify: Boolean ) { - val world = world!!; val pos = pos!! - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity var isPowered = world.getReceivedStrongRedstonePower(pos) > 9 || world.getReceivedRedstonePower(pos) > 9 - val from = fromPos!!.add( 1, 0, 0 ) + val from = fromPos.add( 1, 0, 0 ) + isPowered = isPowered && world.isReceivingRedstonePower(from) - if ( !isPowered || musicPlayer.isTriggered ) { musicPlayer.isTriggered = false; return } + if ( musicPlayer.isPowered == isPowered ) return - musicPlayer.isTriggered = true; musicPlayer.isPlaying = !musicPlayer.isPlaying + musicPlayer.isPowered = isPowered; if ( !isPowered ) return - if ( musicPlayer.isPlaying ) musicPlayer.play() else musicPlayer.pause() + if ( !musicPlayer.isPlaying() ) musicPlayer.play() else { + + musicPlayer.pause(); if ( musicPlayer.repeatOnPlay ) musicPlayer.play() + + } } - override fun getTicker( - world: World?, state: BlockState?, type: BlockEntityType? - ): BlockEntityTicker? { + override fun getTicker( + world: World, state: BlockState, type: BlockEntityType + ): BlockEntityTicker { val id = MusicPlayerBlockEntity.classID() @@ -175,11 +187,11 @@ class MusicPlayerBlock(settings: Settings) : BlockWithEntity(settings), CanBeMut MusicPlayerBlockEntity.tick( world, blockPos ) - } + }!! } - private fun drop( world: World?, pos: BlockPos?, musicPlayer: MusicPlayerBlockEntity ) { + private fun drop( world: World, pos: BlockPos, musicPlayer: MusicPlayerBlockEntity ) { for ( i in 0..16 ) dropStack( world, pos, musicPlayer.getStack(i) ) } @@ -207,9 +219,13 @@ class MusicPlayerBlock(settings: Settings) : BlockWithEntity(settings), CanBeMut val registerBlock = registerBlock( block, defaultSettings() ) var id = MusicPlayerBlockEntity.classID() - val builder1 = FabricBlockEntityTypeBuilder.create( ::MusicPlayerBlockEntity, registerBlock ).build() + val builder1 = FabricBlockEntityTypeBuilder.create( ::MusicPlayerBlockEntity, registerBlock ) + + val registry = Registries.BLOCK + for ( i in 0 until registry.size() ) builder1.addBlock( registry[i] ) + // Because of the tick that checks new positions and that could be weird on any block states. - MusicPlayerBlockEntity.type = Registry.register( Registries.BLOCK_ENTITY_TYPE, id, builder1 ) + MusicPlayerBlockEntity.type = Registry.register( Registries.BLOCK_ENTITY_TYPE, id, builder1.build() ) id = MusicPlayerEntity.classID() val builder2 = FabricEntityTypeBuilder.create( SpawnGroup.MISC, ::MusicPlayerEntity ).build() @@ -228,67 +244,80 @@ class MusicPlayerBlock(settings: Settings) : BlockWithEntity(settings), CanBeMut class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( type, pos, state ), ExtendedScreenHandlerFactory, CustomInventory { - val syncedUsers = mutableSetOf(); var entity: MusicPlayerEntity? = null + val usersListening = mutableSetOf(); var entity: MusicPlayerEntity? = null /** Avoids more than one redstone triggers at the same time. */ - var isTriggered = false; var path = ""; var isPlaying = false + var isPowered = false; var id = this.hashCode(); var repeatOnPlay = false - @Environment(EnvType.CLIENT) var willBreak = false - @Environment(EnvType.CLIENT) var spawnParticles = false - @Environment(EnvType.CLIENT) var isDirectAudio = false - @Environment(EnvType.CLIENT) var sequencer: Sequencer? = null - @Environment(EnvType.CLIENT) var pauseTick: Long = 0 - @Environment(EnvType.CLIENT) var sound: ExternalSound? = null - - /** Linked to the user sync state. It's used for the screen sync button. */ - @Environment(EnvType.CLIENT) var isSynced = false - @Environment(EnvType.CLIENT) var onQuery = false + /** Linked to the user sync / listening state. It's used for the screen sync button. */ + @Environment(EnvType.CLIENT) var isListening = false private val items = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY ) override fun items(): DefaultedList { return items } - override fun readNbt( nbt: NbtCompound? ) { + override fun toInitialChunkDataNbt(): NbtCompound { return createNbt() } + + override fun readNbt(nbt: NbtCompound) { super.readNbt(nbt); Inventories.readNbt( nbt, items ) - } + id = nbt.getInt("ID") + + repeatOnPlay = nbt.getBoolean("repeatOnPlay") + + + val world = world ?: return; if ( !world.isClient ) return - override fun writeNbt( nbt: NbtCompound? ) { + val musicPlayer = MusicPlayer.get(id); val blockEntity = musicPlayer.blockEntity - Inventories.writeNbt( nbt, items ); super.writeNbt(nbt); setup() + if ( blockEntity != null ) { isListening = blockEntity.isListening; entity = blockEntity.entity } + + musicPlayer.blockEntity = this; musicPlayer.setMIDIReceiver() } - override fun markDirty() { super.markDirty() } + override fun writeNbt(nbt: NbtCompound) { - override fun toUpdatePacket(): Packet? { + if ( !nbt.contains("ID") ) nbt.putInt( "ID", id ) - setup(); return BlockEntityUpdateS2CPacket.create(this) + nbt.putBoolean( "repeatOnPlay", repeatOnPlay ) + + Inventories.writeNbt( nbt, items ); super.writeNbt(nbt); init() } + override fun markDirty() { super.markDirty() } + override fun markRemoved() { - willBreak = true; if ( world!!.isClient ) pauseOnClient(true) + val world = world!! + + if ( !world.isClient ) { pause(); MusicPlayer.remove( world, id ) } super.markRemoved() } + override fun toUpdatePacket(): Packet { + + init(); return BlockEntityUpdateS2CPacket.create(this) + + } + // Thinking with hoppers. - override fun canExtract( slot: Int, stack: ItemStack?, direction: Direction? ): Boolean { + override fun canExtract( slot: Int, stack: ItemStack, direction: Direction ): Boolean { - val item = stack!!.item; if ( slot == 16 && item is FloppyDisk ) { pause(); scheduleRead() } + val item = stack.item; if ( slot == 16 && item is FloppyDisk ) { pause(); scheduleRead() } return true } - override fun canInsert( slot: Int, stack: ItemStack?, direction: Direction? ): Boolean { + override fun canInsert( slot: Int, stack: ItemStack, direction: Direction? ): Boolean { - val item = stack!!.item; if ( slot < 16 && item !is Instrument ) return false + val item = stack.item; if ( slot < 16 && item !is Instrument ) return false if ( slot == 16 && item is FloppyDisk ) scheduleRead() else return false @@ -296,8 +325,10 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } - override fun createMenu( syncID: Int, inventory: PlayerInventory?, player: PlayerEntity? ): ScreenHandler { - return MusicPlayerScreenHandler( syncID, inventory!!, this as Inventory ) + override fun createMenu( syncID: Int, inventory: PlayerInventory, player: PlayerEntity ): ScreenHandler { + + return MusicPlayerScreenHandler( syncID, inventory, this as Inventory ) + } override fun getDisplayName(): Text { @@ -306,19 +337,25 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } - override fun writeScreenOpeningData( player: ServerPlayerEntity?, buf: PacketByteBuf? ) { buf!!.writeBlockPos(pos) } + override fun writeScreenOpeningData( player: ServerPlayerEntity, buf: PacketByteBuf ) { buf.writeBlockPos(pos) } companion object : ModID { const val INVENTORY_SIZE = 16 + 1; lateinit var type: BlockEntityType - private val coroutine = CoroutineScope( Dispatchers.IO ) + fun tick( world: World, pos: BlockPos ) { + + val blockEntity = world.getBlockEntity(pos) - private val particles = MusicPlayerEntity.Companion.ActionParticles + if ( blockEntity !is MusicPlayerBlockEntity ) return + + blockEntity.entityTick(); blockEntity.musicPlayerTick() + + } fun networking() { - var id = netID("set_user_sync") + var id = netID("set_user_listening") ServerPlayNetworking.registerGlobalReceiver(id) { server: MinecraftServer, player: ServerPlayerEntity, @@ -330,7 +367,7 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity - val list = musicPlayer.syncedUsers; if (add) list.add(player) else list.remove(player) + val list = musicPlayer.usersListening; if (add) list.add(player) else list.remove(player) } ) @@ -346,222 +383,301 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( server.send( ServerTask( server.ticks ) { - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + val musicPlayer = world.getBlockEntity(pos) ?: return@ServerTask - world.setBlockState( musicPlayer.pos, musicPlayer.cachedState.with( PLAYING, isPlaying ) ) + musicPlayer as MusicPlayerBlockEntity - musicPlayer.isPlaying = isPlaying + musicPlayer.setPlaying(isPlaying) } ) } - id = netID("particles") + id = netID("set_repeat") ServerPlayNetworking.registerGlobalReceiver(id) { server: MinecraftServer, _: ServerPlayerEntity, _: ServerPlayNetworkHandler, buf: PacketByteBuf, _: PacketSender -> - val world = server.overworld; val pos = buf.readBlockPos() + val world = server.overworld; val pos = buf.readBlockPos(); val onRepeat = buf.readBoolean() server.send( ServerTask( server.ticks ) { val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity - val nbt = NBT.get( musicPlayer.getStack(16) ) + musicPlayer.repeatOnPlay = onRepeat; musicPlayer.markDirty() - val allow = serverConfig["music_particles"] as Boolean + } ) - if ( !musicPlayer.isPlaying || !allow ) return@ServerTask + } - val waveType = ( 0 until particles.waves.size ).random() - val buf = PacketByteBufs.create().writeBlockPos(pos) - buf.writeInt(waveType) + MusicPlayer.networking() - musicPlayer.getSyncedUsers(nbt).forEach { + } - ServerPlayNetworking.send( it as ServerPlayerEntity, netID("particles"), buf ) + } - } + @Environment(EnvType.CLIENT) + fun setUserListeningState(isListening: Boolean) { - } ) + val id = netID("set_user_listening") - } + val buf = PacketByteBufs.create().writeBlockPos(pos); buf.writeBoolean(isListening) - if ( !isClient() ) return + ClientPlayNetworking.send( id, buf ) - ClientPlayNetworking.registerGlobalReceiver(id) { + } - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + @Environment(EnvType.CLIENT) + fun setRepeatMode(repeatPlay: Boolean) { - val world = client.world!!; val pos = buf.readBlockPos() - val type = buf.readInt() + val id = netID("set_repeat") - client.send { + val buf = PacketByteBufs.create().writeBlockPos(pos); buf.writeBoolean(repeatPlay) - val allow = clientConfig["music_particles"] as Boolean; if ( !allow ) return@send + ClientPlayNetworking.send( id, buf ) - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + } - musicPlayer.spawnParticles = true + @Environment(EnvType.CLIENT) + fun clientInit() { - musicPlayer.spawnParticles( particles.waves[type] ) + val listenAll = clientConfig["listen_all"] as Boolean - } + if ( !listenAll ) return; isListening = true; setUserListeningState(true) - } + } - id = netID("read") - ClientPlayNetworking.registerGlobalReceiver(id) { + private fun init() { - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + val world = world!!; if ( world.isClient || entity != null ) return - val world = client.world!!; val pos = buf.readBlockPos() + val nextState = world.getBlockState(pos).with( PLAYING, false ) - val stacks = mutableListOf() + world.setBlockState( pos, nextState ) - for ( i in 0 .. 16 ) stacks.add( buf.readItemStack() ) + spawnEntity( MusicPlayerEntity(this) ) - client.send { + } - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + fun isPlaying(): Boolean { return cachedState.get(PLAYING) } - for ( i in 0 .. 16 ) musicPlayer.setStack( i, stacks[i] ) + fun setPlaying(isPlaying: Boolean) { - val floppyStack = musicPlayer.getStack(16); var path = "" + val next = cachedState.with( PLAYING, isPlaying ) - val isEmpty = floppyStack.isEmpty + world!!.setBlockState( pos, next ) - if ( !isEmpty ) path = NBT.get(floppyStack).getString("Path") + } - val isSame = path == musicPlayer.path + /** Get the list of users synced / listening to the block entity, including the owner of the floppy. */ + fun usersListening(floppy: ItemStack): Set { - if ( !isSame || isEmpty ) musicPlayer.pauseOnClient(true) + val users = usersListening.toMutableSet() - musicPlayer.path = path; if ( isEmpty ) return@send + for ( user in users ) if ( user.isRemoved ) usersListening.remove(user) - coroutine.launch { musicPlayer.preload() } + val owner = lookupPlayer( world!!, floppy ) ?: return users - } + if ( !users.contains(owner) ) users.add(owner) - } + return users - id = netID("play") - ClientPlayNetworking.registerGlobalReceiver(id) { + } - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + fun spawnEntity( entity: MusicPlayerEntity ) { - val pos = buf.readBlockPos(); val world = client.world!! + world!!.spawnEntity(entity); entity.init(); this.entity = entity - client.send { + } - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + fun play() { sendAction { - musicPlayer.playOnClient() + val buf = PacketByteBufs.create().writeString("play") - } + buf.writeInt( this.id ); buf - } + } } - id = netID("pause") - ClientPlayNetworking.registerGlobalReceiver(id) { + fun pause() { sendAction { - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + val buf = PacketByteBufs.create().writeString("pause") - val world = client.world!!; val pos = buf.readBlockPos() + buf.writeInt( this.id ); buf - client.send { + } } - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + private fun sendAction( buf: () -> PacketByteBuf ) { - musicPlayer.pauseOnClient() + val floppy = getStack(16); if ( floppy.isEmpty ) return - } + val id = MusicPlayer.netID("action") - } + usersListening(floppy).forEach { ServerPlayNetworking.send( it as ServerPlayerEntity, id, buf() ) } - } + } - fun tick( world: World, pos: BlockPos ) { + private fun scheduleRead() { Timer(5) { read() } } + + private fun read() { read( getStack(16) ) } + + fun read(floppy: ItemStack) { + + val id = MusicPlayer.netID("read"); if ( !NBT.has(floppy) ) return + + val buf = PacketByteBufs.create(); buf.writeInt( this.id ) + + for ( i in 0 .. 16 ) buf.writeItemStack( getStack(i) ) - val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + usersListening(floppy).forEach { ServerPlayNetworking.send( it as ServerPlayerEntity, id, buf ) } - // Entity position. + } + + /** Updates entity position and networks it. */ + private fun entityTick() { + + if ( world!!.isClient ) return; val entity = entity ?: return + + if ( entity.blockPos == pos ) return; entity.setPos(pos) - val entity = musicPlayer.entity + val floppy = getStack(16); val id = MusicPlayer.netID("position") - if ( entity != null && entity.blockPos != pos ) entity.setPosition( Vec3d.of(pos) ) + val buf = PacketByteBufs.create().writeBlockPos(pos); buf.writeInt( this.id ) + + for ( player in usersListening(floppy) ) ServerPlayNetworking.send( player as ServerPlayerEntity, id, buf ) + + } - // Sequencer. + private fun musicPlayerTick() { - val floppyStack = musicPlayer.getStack(16) + val floppy = getStack(16) - if ( !world.isClient || floppyStack.isEmpty ) return + if ( !world!!.isClient ) { - musicPlayer.sequencerTick() + val isEmpty = usersListening(floppy).isEmpty() && isPlaying() + + if (isEmpty) setPlaying(false); return } + MusicPlayer.get(id).tick() + } - @Environment(EnvType.CLIENT) - fun inputExists(): Boolean { return isValidUrl(path) || ModFile(path).exists() } +} - @Environment(EnvType.CLIENT) - fun setUserSyncStatus(isSynced: Boolean) { +/** This entity used as instruments holder and for particles. */ +class MusicPlayerEntity( type: EntityType, world: World ) : Entity( type, world ) { - val id = MusicPlayerBlockEntity.netID("set_user_sync") + constructor( blockEntity: MusicPlayerBlockEntity ) : this( Companion.type, blockEntity.world!! ) { - val buf = PacketByteBufs.create().writeBlockPos(pos) - buf.writeBoolean(isSynced) + setPos( blockEntity.pos ) - ClientPlayNetworking.send( id, buf ) + } + + override fun initDataTracker() {} + + override fun readCustomDataFromNbt(nbt: NbtCompound) {} + + override fun writeCustomDataToNbt(nbt: NbtCompound) {} + + override fun onSpawnPacket(packet: EntitySpawnS2CPacket) { + + super.onSpawnPacket(packet) + + val musicPlayer = world.getBlockEntity(blockPos) + + if ( musicPlayer !is MusicPlayerBlockEntity ) return + + val entity = musicPlayer.entity; if ( entity != null && !entity.isRemoved ) return + + musicPlayer.spawnEntity(this); musicPlayer.clientInit() } - @Environment(EnvType.CLIENT) - fun playOnClient() { + override fun createSpawnPacket(): Packet { return EntitySpawnS2CPacket(this) } - if ( path.isEmpty() ) return; modPrint("$entity: Tried to play.") + override fun getName(): Text { return Text.of( Translation.block("music_player") ) } - val nbt = NBT.get( getStack(16) ); val player = lookupPlayer(nbt) ?: return + fun init() { - var isPlaying = false; val isFormer = player() == player + val facing = world.getBlockState(blockPos).get(FACING) - if (isFormer) isPlaying = playMidi(); val playSound = playSound() + this.yaw = facing.asRotation() - isPlaying = isPlaying || playSound + } - // Let the former player change only the state of the block. + fun setPos(blockPos: BlockPos) { - if (isFormer) { setPlayingState(isPlaying); postPlay() } + val newPos = Vec3d.of(blockPos).add( 0.5, 0.0, 0.5 ) - if (playSound) this.isPlaying = true + setPosition(newPos) } - @Environment(EnvType.CLIENT) - private fun postPlay() { + companion object : ModID { + + lateinit var type: EntityType + + class Renderer( context: EntityRendererFactory.Context ) : EntityRenderer(context) { + override fun getTexture( entity: MusicPlayerEntity ): Identifier { return Identifier("") } + } + + } + +} - if ( !isPlaying ) return; val id = netID("particles") +/** Handles all the music playback. Used for the clientside only. */ +class MusicPlayer( val id: Int ) { - val buf = PacketByteBufs.create().writeBlockPos(pos) + var blockEntity: MusicPlayerBlockEntity? = null; var path = "" + + private var sequencer: Sequencer? = null; private var isPlaying = false + + private var pauseTick: Long = 0; var spawnParticles = false + + private var onQuery = false; private var isDirectAudio = false + + var sound: ExternalSound? = null; init { list.add(this) } + + val items: DefaultedList = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY ) + + private val actions = mapOf( "play" to ::play, "pause" to ::pause ) + + override fun toString(): String { return "Music Player: ${ pos() }" } + + fun stopSequencer() { sequencer!!.stop() }; fun pos(): BlockPos { return blockEntity!!.pos } + + fun isFormerPlayer(): Boolean { + + val floppy = items[16]; val player = lookupPlayer( world()!!, floppy ) + + return player() == player + + } + + private fun setPlaying(isPlaying: Boolean) { + + val floppy = items[16]; this.isPlaying = isPlaying + + if ( !isFormerPlayer() && !floppy.isEmpty ) return + + val id = MusicPlayerBlockEntity.netID("set_playing_state") + + val buf = PacketByteBufs.create().writeBlockPos( pos() ); buf.writeBoolean(isPlaying) ClientPlayNetworking.send( id, buf ) } + private fun isMidi(): Boolean { return path.endsWith(".mid") } + // Local files are linked to the last player having the floppy. - // Online files are linked by the sync button. + // Online files are linked by the listening button. - @Environment(EnvType.CLIENT) private fun playMidi(): Boolean { - if ( !path.endsWith(".mid") || !hasSequencer() ) return false + if ( !isMidi() || !hasSequencer() ) return false val sequencer = sequencer!!; val file = ModFile(path) @@ -574,78 +690,143 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } catch ( e: Exception ) { warnUser( FloppyDisk.missingMessage(path) ) + warnUser( Translation.get("message.check_console") ) e.printStackTrace(); return false } - sequencer.start(); sequencer.tickPosition = pauseTick; return true + sequencer.start(); sequencer.tickPosition = pauseTick + + return true } /** Try to load direct url or local file. */ - @Environment(EnvType.CLIENT) private fun playSound(): Boolean { - if ( path.endsWith(".mid") ) return false + if ( isMidi() ) return false val warning = Translation.get("message.file_on_query") if (onQuery) { warnUser(warning); return false } - loadSound(); val sound = sound ?: return false + loadSound(); val sound = sound!! + + if ( !sound.isValid() ) return false sound.play(); return true } - /** Preloads yt-dl requests. (for now) */ - @Environment(EnvType.CLIENT) - fun preload() { + private fun loadSound() { - Thread.currentThread().name = "HonkyTones Preload thread" + if ( !inputExists() ) return - val path = path; isDirectAudio = false + sound = try { ExternalSound(this) } catch ( e: Exception ) { - val validURL = isValidUrl(path); if ( !validURL ) return + warnUser( Translation.get("error.file_access") ) - val connection = URL(path).openConnection() + warnUser( Translation.get("message.check_console") ) - isDirectAudio = connection.contentType.contains("audio") + e.printStackTrace(); null - if (isDirectAudio) { modPrint( "$entity: Direct Stream Content Type: " + connection.contentType ); return } + } - if ( isCached(path) ) return; onQuery = true + } - modPrint("$entity: Starting request...") + fun play() { + + if ( path.isEmpty() ) return + + var playMidi = false; if ( isFormerPlayer() ) playMidi = playMidi() - // Download source using yt-dl + ffmpeg. + val isPlaying = playMidi || playSound(); setPlaying(isPlaying) - val info = infoRequest(path) ?: return + if ( isMidi() && !isFormerPlayer() ) statusMessage("Listening...") + + if ( !isPlaying ) return; statusMessage("Playing...") + + startParticles() + + } + + private fun startParticles() { + + if ( !isFormerPlayer() ) return; val id = netID("particles") + + val buf = PacketByteBufs.create().writeBlockPos( pos() ); buf.writeInt( this.id ) + + ClientPlayNetworking.send( id, buf ) + + } + + fun pause() { pause( blockEntity!!.repeatOnPlay ) } + + fun pause(stop: Boolean) { + + spawnParticles = false; if ( !isPlaying ) return; setPlaying(false) + + if ( hasSound() ) sound!!.fadeOut() else { + + if ( !hasSequencer() ) return; val sequencer = sequencer!! + + pauseTick = sequencer.tickPosition; if (stop) pauseTick = 0 + + for ( i in 0..15 ) { + + val stack = items[16]; val item = stack.item + + if ( item is Instrument ) item.stopDeviceSounds(stack) + + } + + sequencer.stop() + + } + + statusMessage("Stopping...") + + } + + fun pauseOnMidiHost() { if ( !isFormerPlayer() || !isMidi() ) return; pause() } + + /** Loads youtube-dl requests. */ + fun read() { + + Thread.currentThread().name = "HonkyTones Loading thread"; isDirectAudio = false + + val validURL = isValidUrl(path); if ( !validURL ) return + + val connection = URL(path).openConnection(); val type = connection.contentType + + isDirectAudio = type != null && type.contains("audio") + + if (isDirectAudio) { statusMessage( "Direct Stream Format: " + connection.contentType + ":" ); return } + + if ( isCached(path) ) return; onQuery = true; modPrint("$this: Starting request...") + + val info = infoRequest(path) ?: return // Download sources using yt-dl + ffmpeg. val max = clientConfig["max_length"] as Int // Limit to max_length in config. if ( info.duration > max ) { - val s = Translation.get("error.long_stream") + val warning = Translation.get("error.long_stream") .replace( "X", "${ max / 60f }" ) - warnUser(s); return + warnUser(warning); return } - val streamsPath = directories["streams"]!!.path - - var filePath = "$streamsPath\\" + val streamsPath = directories["streams"]!!.path; var filePath = "$streamsPath\\" var name = info.id + "-" + info.title + ".ogg" name = name.replace( Regex("[\\\\/:*?\"<>|]"), "_" ) .replace( " ", "_" ) - filePath += name; val outputFile = ModFile(filePath) - - outputFile.createNewFile() + filePath += name; val outputFile = ModFile(filePath); outputFile.createNewFile() try { @@ -690,11 +871,8 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } - onQuery = false - } - @Environment(EnvType.CLIENT) private fun requestVideo(outputPath: String) { val request = MediaRequest(path) @@ -707,38 +885,8 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } - @Environment(EnvType.CLIENT) fun pauseOnClient() { pauseOnClient(false) } - - @Environment(EnvType.CLIENT) - fun pauseOnClient(stop: Boolean) { - - spawnParticles = false; if ( !isPlaying ) return; setPlayingState(false) - - if ( hasSound() ) sound!!.fadeOut() else { - - if ( !hasSequencer() ) return; val sequencer = sequencer!! - - pauseTick = sequencer.tickPosition; if (stop) pauseTick = 0 - - sequencer.stop() - - for ( i in 0..15 ) { - - val stack = getStack(i); val item = stack.item - - if ( item is Instrument ) item.stopDeviceSounds(stack) - - } - - modPrint("$entity: Stopped.") - - } - - } - /** Verifies if the file was already downloaded. */ - @Environment(EnvType.CLIENT) - fun isCached(path: String): Boolean { + private fun isCached(path: String): Boolean { val directory = directories["streams"]!! @@ -764,253 +912,302 @@ class MusicPlayerBlockEntity( pos: BlockPos, state: BlockState ) : BlockEntity( } - @Environment(EnvType.CLIENT) - private fun loadSound() { - - if ( !inputExists() ) return + fun inputExists(): Boolean { return isValidUrl(path) || ModFile(path).exists() } - try { + fun setMIDIReceiver() { - val sound = ExternalSound( path, this ) + if ( sequencer != null || !Midi.hasSystemSequencer() ) return - sound.entity = entity; this.sound = sound + sequencer = MidiSystem.getSequencer() - } catch ( e: Exception ) { + val sequencer = sequencer!!; if ( !sequencer.isOpen ) sequencer.open() - warnUser( Translation.get("error.file_access") ) - warnUser( Translation.get("message.check_console") ) + val transmitters = sequencer.transmitters - e.printStackTrace(); sound = null + for ( transmitter in transmitters ) transmitter.receiver = MusicPlayerReceiver(this) - } + sequencer.transmitter.receiver = MusicPlayerReceiver(this) } - @Environment(EnvType.CLIENT) - fun setPlayingState(isPlaying: Boolean) { + fun spawnParticles(wave: ParticleEffect) { - if (willBreak) return; this.isPlaying = isPlaying + val allow = clientConfig["music_particles"] as Boolean - val id = netID("set_playing_state") + if ( !spawnParticles || !allow ) return - val buf = PacketByteBufs.create().writeBlockPos(pos) - buf.writeBoolean(isPlaying) + val l1 = Random.nextInt(10) + val l2 = Random.nextInt( 10, 15 ) + val l3 = Random.nextInt( 10, 15 ) + val l4 = Random.nextInt( 5, 15 ) - ClientPlayNetworking.send( id, buf ) + if ( blockEntity!!.repeatOnPlay ) spawnParticles = false + + Timer(l4) { spawnParticles(wave) } + + val blockEntity = blockEntity ?: return + + val entity = blockEntity.entity ?: return + + val distance = Particles.MIN_DISTANCE + + val isNear = player()!!.blockPos.isWithinDistance( entity.pos, distance ) + + if ( isMuted(entity) || !isNear ) return + + Timer(l1) { ActionParticles.spawnNote(entity) } + + Timer(l2) { ActionParticles.spawnWave( entity, wave, false ) } + + Timer(l3) { ActionParticles.spawnWave( entity, wave, true ) } } - @Environment(EnvType.CLIENT) - private fun sequencerTick() { + fun tick() { midiTick() } + + private fun midiTick() { + + if ( hasSound() || !hasSequencer() ) return - val floppyStack = getStack(16) + val floppy = items[16]; val sequencer = sequencer!! - if ( hasSound() || floppyStack.isEmpty || !hasSequencer() ) return + if ( floppy.isEmpty ) { if ( sequencer.isRunning ) sendPause(); return } - val nbt = NBT.get(floppyStack); val sequencer = sequencer!! + val nbt = NBT.get(floppy) sequencer.tempoFactor = nbt.getFloat("Rate") - if ( !sequencer.isRunning && isPlaying ) pauseOnClient(true) + val sequence = sequencer.sequence ?: return + + val finished = sequencer.tickPosition == sequence.tickLength + + if ( isPlaying && finished ) sendPause() } - @Environment(EnvType.CLIENT) fun hasSequencer(): Boolean { return sequencer != null } + private fun sendPause() { - @Environment(EnvType.CLIENT) fun hasSound(): Boolean { return sound != null } + sequencer!!.tickPosition = 0 - @Environment(EnvType.CLIENT) - private fun loadReceiver() { + val id = netID("send_pause"); val buf = PacketByteBufs.create() - if ( !Midi.hasSystemSequencer() ) return; sequencer = MidiSystem.getSequencer() + buf.writeBlockPos( pos() ); ClientPlayNetworking.send( id, buf ) - val sequencer = sequencer!!; if ( !sequencer.isOpen ) sequencer.open() + } - val transmitters = sequencer.transmitters + private fun hasSequencer(): Boolean { return sequencer != null } - for ( transmitter in transmitters ) transmitter.receiver = MusicPlayerReceiver(this) + private fun hasSound(): Boolean { return sound != null } - sequencer.transmitter.receiver = MusicPlayerReceiver(this) + private fun statusMessage( statusType: String ) { + + if ( blockEntity!!.repeatOnPlay ) return + + modPrint("$this: $statusType \"$path\"") } - private fun setup() { + companion object : ModID { - val world = world!! + val list = mutableListOf() - if ( world.isClient || entity != null ) return + private fun create(id: Int): MusicPlayer { - entity = MusicPlayerEntity(this) + val musicPlayer = MusicPlayer(id); list.add(musicPlayer) - entity!!.setup(); world.spawnEntity(entity) + return musicPlayer - val nextState = world.getBlockState(pos).with( PLAYING, false ) + } - world.setBlockState( pos, nextState ) + fun get(id: Int): MusicPlayer { - } + val musicPlayer = list.find { it.id == id } - private fun lookupPlayer(nbt: NbtCompound): PlayerEntity? { + if ( musicPlayer != null ) return musicPlayer - val players = world!!.players + return create(id) - return players.find { it.id == nbt.getInt("PlayerID") } + } - } + fun remove( world: World, id: Int ) { - /** Get the list of users synced to the block entity, including the owner of the floppy. */ - private fun getSyncedUsers(nbt: NbtCompound): MutableSet { + val buf = PacketByteBufs.create(); val players = world.players - val users = syncedUsers.toMutableSet() + buf.writeInt(id); val id = netID("remove") - for ( user in users ) if ( user.isRemoved ) syncedUsers.remove(user) + players.forEach { ServerPlayNetworking.send( it as ServerPlayerEntity, id, buf ) } - val owner = lookupPlayer(nbt) ?: return users + } - if ( !users.contains(owner) ) users.add(owner) + fun networking() { - return users + var id = netID("send_pause") + ServerPlayNetworking.registerGlobalReceiver(id) { - } + server: MinecraftServer, _: ServerPlayerEntity, + _: ServerPlayNetworkHandler, buf: PacketByteBuf, _: PacketSender -> - fun play() { + val world = server.overworld; val pos = buf.readBlockPos() - isPlaying = true; val floppyStack = getStack(16) + server.send( ServerTask( server.ticks ) { - if ( floppyStack.isEmpty ) return; val nbt = NBT.get(floppyStack) + val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity - val buf = PacketByteBufs.create().writeBlockPos(pos) + musicPlayer.pause() - getSyncedUsers(nbt).forEach { + } ) - ServerPlayNetworking.send( it as ServerPlayerEntity, netID("play"), buf ) + } - } + id = netID("particles") + ServerPlayNetworking.registerGlobalReceiver(id) { - } + server: MinecraftServer, _: ServerPlayerEntity, + _: ServerPlayNetworkHandler, buf: PacketByteBuf, _: PacketSender -> - @Environment(EnvType.CLIENT) - fun initClient() { + val world = server.overworld; val pos = buf.readBlockPos(); val id = buf.readInt() - loadReceiver(); val syncAll = clientConfig["sync_all"] as Boolean + server.send( ServerTask( server.ticks ) { - if ( !syncAll ) return; isSynced = true + val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity - setUserSyncStatus(true) + val floppy = musicPlayer.getStack(16) - } + val allow = serverConfig["music_particles"] as Boolean - @Environment(EnvType.CLIENT) - private fun spawnParticles( wave: ParticleEffect ) { + if ( !musicPlayer.isPlaying() || !allow ) return@ServerTask - if ( !spawnParticles || isRemoved ) return + val waveType = ActionParticles.randomWave() - val entity = entity!! + val buf = PacketByteBufs.create(); buf.writeInt(waveType); buf.writeInt(id) - val l1 = Random.nextInt(10) - val l2 = Random.nextInt( 10, 15 ) - val l3 = Random.nextInt( 10, 15 ) - val l4 = Random.nextInt( 5, 15 ) + musicPlayer.usersListening(floppy).forEach { - Timer(l4) { spawnParticles(wave) } + ServerPlayNetworking.send( it as ServerPlayerEntity, netID("particles"), buf ) - if ( isMuted(entity) ) return + } - Timer(l1) { particles.spawnNote(entity) } + } ) - Timer(l2) { particles.spawnWave( entity, wave, false ) } + } - Timer(l3) { particles.spawnWave( entity, wave, true ) } + if ( !isClient() ) return - } + ClientPlayNetworking.registerGlobalReceiver(id) { - fun pause() { + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - isPlaying = false; val floppyStack = getStack(16) + val type = buf.readInt(); val id = buf.readInt() - if ( floppyStack.isEmpty ) return; val nbt = NBT.get(floppyStack) + client.send { - val id = netID("pause"); val buf = PacketByteBufs.create().writeBlockPos(pos) + val musicPlayer = get(id); musicPlayer.spawnParticles = true - for ( player in getSyncedUsers(nbt) ) ServerPlayNetworking.send( player as ServerPlayerEntity, id, buf ) + musicPlayer.spawnParticles( ActionParticles.waves[type] ) - } + } - private fun scheduleRead() { Timer(5) { read() } } + } - private fun read() { read( getStack(16) ) } - fun read(floppy: ItemStack) { + id = netID("read") + ClientPlayNetworking.registerGlobalReceiver(id) { - val id = netID("read"); if ( !NBT.has(floppy) ) return + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - val nbt = NBT.get(floppy); val buf = PacketByteBufs.create() + val id = buf.readInt(); val stacks = mutableListOf() - buf.writeBlockPos(pos) + for ( i in 0 .. 16 ) stacks.add( buf.readItemStack() ) - for ( i in 0 .. 16 ) buf.writeItemStack( getStack(i) ) + client.send { - for ( player in getSyncedUsers(nbt) ) { + val musicPlayer = get(id); val items = musicPlayer.items - ServerPlayNetworking.send( player as ServerPlayerEntity, id, buf ) + val blockEntity = musicPlayer.blockEntity - } + for ( i in 0 .. 16 ) { - } + val stack = stacks[i]; items[i] = stack -} + blockEntity!!.setStack( i, stack ) -/** This entity is created mainly to be set as the instruments stacks holder, so instruments can be played. */ -class MusicPlayerEntity( type: EntityType, world: World ) : Entity( type, world ) { + } - constructor( blockEntity: MusicPlayerBlockEntity ) : this( Companion.type, blockEntity.world!! ) { + val floppy = items[16]; var path = "" - val pos = Vec3d.of( blockEntity.pos ); setPosition( pos.add( 0.5, 0.0, 0.5 ) ) + val isEmpty = floppy.isEmpty - } + if ( !isEmpty ) path = NBT.get(floppy).getString("Path") - override fun tickInVoid() {}; override fun initDataTracker() {} + val isSame = path == musicPlayer.path - override fun readCustomDataFromNbt( nbt: NbtCompound? ) {} + if ( !isSame || isEmpty ) musicPlayer.pause(true) - override fun writeCustomDataToNbt( nbt: NbtCompound? ) {} + musicPlayer.path = path; if (isEmpty) return@send - override fun onSpawnPacket( packet: EntitySpawnS2CPacket? ) { + coroutine.launch { musicPlayer.read(); musicPlayer.onQuery = false } - super.onSpawnPacket(packet) + } - val musicPlayer = world.getBlockEntity(blockPos) ?: return + } - musicPlayer as MusicPlayerBlockEntity + id = netID("position") + ClientPlayNetworking.registerGlobalReceiver(id) { - world.spawnEntity(this); musicPlayer.initClient() + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - musicPlayer.entity = this + val newPos = buf.readBlockPos(); val id = buf.readInt() - } + client.send { - override fun createSpawnPacket(): Packet? { return EntitySpawnS2CPacket(this) } + val entity = get(id).blockEntity!!.entity!! - override fun getName(): Text { return Text.of("MusicPlayer") } + entity.setPos(newPos) - fun setup() { + } - val facing = world.getBlockState(blockPos).get(FACING); this.yaw = facing.asRotation() + } - } + id = netID("remove") + ClientPlayNetworking.registerGlobalReceiver(id) { - companion object : ModID { + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - lateinit var type: EntityType + val id = buf.readInt() + + client.send { + + val musicPlayer = list.find { it.id == id } ?: return@send + + list.remove(musicPlayer) + + } + + } + + id = netID("action") + ClientPlayNetworking.registerGlobalReceiver(id) { + + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> + + val name = buf.readString(); val id = buf.readInt() + + client.send { get(id).actions[name]!!() } + + } - class Renderer( context: EntityRendererFactory.Context? ) : EntityRenderer(context) { - override fun getTexture( entity: MusicPlayerEntity? ): Identifier { return Identifier("") } } object ActionParticles { val waves = listOf( WAVE1, WAVE2, WAVE3, WAVE4 ) - @Environment(EnvType.CLIENT) + fun randomWave(): Int { return waves.indices.random() } + fun spawnNote( entity: Entity ) { world() ?: return @@ -1023,7 +1220,6 @@ class MusicPlayerEntity( type: EntityType, world: World ) : E } - @Environment(EnvType.CLIENT) fun spawnWave( entity: Entity, wave: ParticleEffect, flip: Boolean ) { world() ?: return diff --git a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Receiver.kt b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Receiver.kt index a307397..eb4b122 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Receiver.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Receiver.kt @@ -5,23 +5,24 @@ import com.enginemachiner.honkytones.GenericReceiver import com.enginemachiner.honkytones.items.instruments.Instrument import com.enginemachiner.honkytones.modPrint import com.enginemachiner.honkytones.sound.InstrumentSound +import com.enginemachiner.honkytones.world import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.minecraft.entity.Entity import net.minecraft.item.ItemStack @Environment(EnvType.CLIENT) -class MusicPlayerReceiver( private val blockEntity: MusicPlayerBlockEntity ) : GenericReceiver() { +class MusicPlayerReceiver( private val musicPlayer: MusicPlayer ) : GenericReceiver() { override fun close() { modPrint("$entity: Device has been closed.") } override fun setData() { - entity = blockEntity.entity!! + entity = musicPlayer.blockEntity!!.entity!! val instruments = mutableListOf() - for ( i in 0..15 ) instruments.add( blockEntity.getStack(i) ) + for ( i in 0..15 ) instruments.add( musicPlayer.items[i] ) this.instruments = instruments @@ -29,6 +30,14 @@ class MusicPlayerReceiver( private val blockEntity: MusicPlayerBlockEntity ) : G override fun canPlay( stack: ItemStack, channel: Int ): Boolean { + if ( world() == null ) { + + musicPlayer.stopSequencer(); musicPlayer.spawnParticles = false + + return false + + } + val instrument = stack.item; val index = instruments.indexOf(stack) return instrument is Instrument && index == channel @@ -41,7 +50,7 @@ class MusicPlayerReceiver( private val blockEntity: MusicPlayerBlockEntity ) : G if ( isMuted(entity) ) { - instrument.stopDeviceSounds(stack); setData() + instrument.stopDeviceSounds(stack); checkHolder( stack, entity ) sound.setData(stack); sound.playOnClients() diff --git a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Screen.kt index 5b41fe5..5e34b22 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/blocks/musicplayer/Screen.kt @@ -37,9 +37,9 @@ class MusicPlayerScreenHandler( syncID: Int, playerInventory: PlayerInventory, private val inventory: Inventory ) : StrictSlotScreen( type, syncID ) { - private val player = playerInventory.player; val world: World = player.world + private val player = playerInventory.player; val world: World = player.world - var pos: BlockPos? = null + @Environment(EnvType.CLIENT) var forceListen = "false"; var pos: BlockPos? = null constructor( syncID: Int, playerInventory: PlayerInventory, buf: PacketByteBuf ) : this( syncID, playerInventory, SimpleInventory(INVENTORY_SIZE) ) { @@ -137,9 +137,9 @@ class MusicPlayerScreenHandler( } - override fun close(player: PlayerEntity?) { inventory.markDirty(); super.close(player) } + override fun close(player: PlayerEntity) { inventory.markDirty(); super.close(player) } - override fun quickMove( player: PlayerEntity?, slotIndex: Int ): ItemStack { + override fun quickMove( player: PlayerEntity, slotIndex: Int ): ItemStack { val slot = slots[slotIndex] @@ -149,15 +149,13 @@ class MusicPlayerScreenHandler( if ( !transfer ) return ItemStack.EMPTY - updateClients(16) - - return slot.stack.copy() + updateClients(16); return slot.stack.copy() } /** Place instruments and floppy disks only and move inventory freely. */ override fun onSlotClick( - slotIndex: Int, button: Int, actionType: SlotActionType?, player: PlayerEntity? + slotIndex: Int, button: Int, actionType: SlotActionType, player: PlayerEntity ) { fun onSlotClick() { super.onSlotClick( slotIndex, button, actionType, player ) } @@ -183,7 +181,7 @@ class MusicPlayerScreenHandler( } - override fun canUse( player: PlayerEntity? ): Boolean { return inventory.canPlayerUse(player) } + override fun canUse(player: PlayerEntity): Boolean { return inventory.canPlayerUse(player) } companion object: ModID { @@ -227,13 +225,13 @@ class MusicPlayerScreenHandler( private fun updateClients(slotIndex: Int) { - if ( world.isClient ) return; var floppyStack = stacks[16] + if ( world.isClient ) { if ( forceListen == "false" ) forceListen = "true"; return } - val musicPlayer = inventory as MusicPlayerBlockEntity + var floppy = stacks[16]; val musicPlayer = inventory as MusicPlayerBlockEntity - if ( floppyStack.nbt == null ) floppyStack = cursorStack + if ( !NBT.has(floppy) ) floppy = cursorStack - trackPos(slotIndex); musicPlayer.read(floppyStack) + trackPos(slotIndex); musicPlayer.read(floppy) } @@ -248,7 +246,8 @@ class MusicPlayerScreen( private val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity - private var syncButton: ButtonWidget? = null + private var listenButton: ButtonWidget? = null + private var repeatButton: ButtonWidget? = null private var slider = Slider( 30, 15, 100, 20, handler ) private val genericTexture = Identifier("textures/gui/container/generic_54.png") @@ -270,23 +269,41 @@ class MusicPlayerScreen( val on = Translation.get("gui.on"); val off = Translation.get("gui.off") - val downloadsText = Translation.block("music_player.downloads") - - val isSynced = musicPlayer.isSynced val switch = mutableMapOf( true to on, false to off ) - syncButton = createButton( x, y, - w2 * 1.8f, height * 0.65f, w, h, w2, 10f ) { - musicPlayer.isSynced = !musicPlayer.isSynced + val listenButtonTitle = Translation.block("music_player.listen") + + val isListening = musicPlayer.isListening + listenButton = createButton( x, y, - w2 * 1.8f, height * 0.65f, w, h, (w2 * 0.75f).toInt(), 10f ) { - val isSynced = musicPlayer.isSynced; musicPlayer.setUserSyncStatus(isSynced) + musicPlayer.isListening = !musicPlayer.isListening - it.message = Text.of("$downloadsText: ${ switch[isSynced] }") + val isListening = musicPlayer.isListening; musicPlayer.setUserListeningState(isListening) + + it.message = Text.of("$listenButtonTitle: ${ switch[isListening] }") } - syncButton!!.message = Text.of("$downloadsText: ${ switch[isSynced] }") + listenButton!!.message = Text.of("$listenButtonTitle: ${ switch[isListening] }") + + addDrawableChild(listenButton) + + val repeatButtonTitle = Translation.block("music_player.repeat") - addDrawableChild(syncButton); super.init() + val repeatPlay = musicPlayer.repeatOnPlay + repeatButton = createButton( width - x, y, - w2 * 2.2f, height * 0.65f, w, h, w2, 10f ) { + + musicPlayer.repeatOnPlay = !musicPlayer.repeatOnPlay + + val repeatPlay = musicPlayer.repeatOnPlay; musicPlayer.setRepeatMode(repeatPlay) + + it.message = Text.of("$repeatButtonTitle: ${ switch[repeatPlay] }") + + } + + repeatButton!!.message = Text.of("$repeatButtonTitle: ${ switch[repeatPlay] }") + + addDrawableChild(repeatButton); super.init() } @@ -306,7 +323,7 @@ class MusicPlayerScreen( } - override fun drawBackground( matrices: MatrixStack?, delta: Float, mouseX: Int, mouseY: Int ) { + override fun drawBackground( matrices: MatrixStack, delta: Float, mouseX: Int, mouseY: Int ) { RenderSystem.setShader( GameRenderer::getPositionTexProgram ) RenderSystem.setShaderColor( 1f, 1f, 1f, 1f ) @@ -341,7 +358,17 @@ class MusicPlayerScreen( } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun handledScreenTick() { + + val forceState = handler.forceListen == "true" && !musicPlayer.isListening + + if (forceState) { listenButton!!.onPress(); handler.forceListen = "done" } + + if ( world.getBlockEntity(pos) !is MusicPlayerBlockEntity ) close() + + } + + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices); super.render( matrices, mouseX, mouseY, delta ) @@ -388,15 +415,17 @@ class MusicPlayerScreen( private val pos = handler.pos; private val world = handler.world - private val musicPlayer = world.getBlockEntity(pos) as MusicPlayerBlockEntity + private val blockEntity = world.getBlockEntity(pos) as MusicPlayerBlockEntity - private var floppyStack = musicPlayer.getStack(16) + private val musicPlayer = MusicPlayer.get( blockEntity.id ) + + private var floppy = blockEntity.getStack(16) private var init = false private var title = ""; private var valueText = 1 private var key = ""; private var scale = 2f - init { visible = false } + init { visible = false; musicPlayer.items[16] = floppy } override fun mouseDragged( mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double ): Boolean { @@ -426,7 +455,7 @@ class MusicPlayerScreen( override fun applyValue() { - val nbt = NBT.get(floppyStack); val value1 = value * scale + val nbt = NBT.get(floppy); val value1 = value * scale if ( nbt.getFloat(key).toDouble() == value1 && init ) return @@ -438,17 +467,19 @@ class MusicPlayerScreen( } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { super.render( matrices, mouseX, mouseY, delta ) - floppyStack = musicPlayer.getStack(16) + floppy = blockEntity.getStack(16) + + visible = !floppy.isEmpty; if ( !visible ) return - visible = !floppyStack.isEmpty; if ( !visible ) return + visible = musicPlayer.inputExists() && !isMuted( blockEntity.entity!! ) - visible = musicPlayer.inputExists() && !isMuted( musicPlayer.entity!! ) + if ( !musicPlayer.isFormerPlayer() && key == "Rate" ) visible = false - check(floppyStack) + check(floppy) } @@ -482,7 +513,7 @@ class MusicPlayerScreen( private fun getValue(): Double { - return NBT.get(floppyStack).getFloat(key).toDouble() + return NBT.get(floppy).getFloat(key).toDouble() } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/console/DigitalConsole.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/console/DigitalConsole.kt index a8bf217..d28ab8c 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/console/DigitalConsole.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/console/DigitalConsole.kt @@ -16,11 +16,11 @@ import net.minecraft.world.World class DigitalConsole : Item( defaultSettings().maxDamage(6) ), StackMenu { - override fun use( world: World?, user: PlayerEntity?, hand: Hand? ): TypedActionResult { + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { - val stack = user!!.getStackInHand(hand); val canOpen = canOpenMenu( user, stack ) + val stack = user.getStackInHand(hand); val canOpen = canOpenMenu( user, stack ) - val action = TypedActionResult.pass(stack); if ( world!!.isClient || !canOpen ) return action + val action = TypedActionResult.pass(stack); if ( world.isClient || !canOpen ) return action user.openHandledScreen( createMenu(stack) ) @@ -37,18 +37,18 @@ class DigitalConsole : Item( defaultSettings().maxDamage(6) ), StackMenu { override fun trackTick( stack: ItemStack, slot: Int ) { trackHand(stack) } override fun inventoryTick( - stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean + stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { super.inventoryTick( stack, world, entity, slot, selected ) - val nbt = NBT.get(stack!!) + val nbt = NBT.get(stack) - if ( world!!.isClient || !nbt.contains("damage") ) return + if ( world.isClient || !nbt.contains("damage") ) return entity as PlayerEntity; nbt.remove("damage") - stack.damage( 1, entity ) { breakEquipment(entity, stack) } + stack.damage( 1, entity ) { breakEquipment( entity, stack ) } } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/console/RecordingScreen.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/console/RecordingScreen.kt index ad61d13..c216e2a 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/console/RecordingScreen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/console/RecordingScreen.kt @@ -60,7 +60,7 @@ class RecordingScreen( private val screen: DigitalConsoleScreen ) : Screen( Text screen.recordingFileName = fileName; sequencer!!.start() - screen.channel = channelField!!.text.toInt() + screen.channel = channelField!!.text.toInt() - 1 val nbt = NBT.get(stack); nbt.putBoolean( "damage", true ) @@ -82,7 +82,7 @@ class RecordingScreen( private val screen: DigitalConsoleScreen ) : Screen( Text override fun tick() { channelField!!.tick() } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices); super.render( matrices, mouseX, mouseY, delta ) @@ -96,17 +96,9 @@ class RecordingScreen( private val screen: DigitalConsoleScreen ) : Screen( Text if ( ModFile(path).isFile ) overwrite = "($overwriteTitle)" - textRenderer.draw( - matrices, "$fileNameTitle: $overwrite", - fileNameField!!.x.toFloat(), fileNameField!!.y.toFloat() - 12, - 0xFFFFFF - ) + textRenderer.draw( matrices, "$fileNameTitle: $overwrite", fileNameField!!.x.toFloat(), fileNameField!!.y.toFloat() - 12, 0xFFFFFF ) - textRenderer.draw( - matrices, "$channelTitle:", - channelField!!.x.toFloat(), channelField!!.y.toFloat() - 12, - 0xFFFFFF - ) + textRenderer.draw( matrices, "$channelTitle:", channelField!!.x.toFloat(), channelField!!.y.toFloat() - 12, 0xFFFFFF ) } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/console/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/console/Screen.kt index 6dec3e4..5746b2d 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/console/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/console/Screen.kt @@ -4,7 +4,6 @@ import com.enginemachiner.honkytones.* import com.enginemachiner.honkytones.Init.Companion.MOD_NAME import com.enginemachiner.honkytones.Init.Companion.directories import com.enginemachiner.honkytones.items.instruments.Instrument -import com.enginemachiner.honkytones.items.instruments.particles import com.mojang.blaze3d.systems.RenderSystem import net.fabricmc.api.EnvType import net.fabricmc.api.Environment @@ -46,6 +45,8 @@ import javax.sound.midi.MidiSystem import javax.sound.midi.Sequence import javax.sound.midi.ShortMessage +private val particles = Instrument.Companion.ActionParticles + class DigitalConsoleScreenHandler( syncID: Int, private val playerInventory: PlayerInventory, val inventory: Inventory ) : ScreenHandler( type, syncID ) { @@ -78,7 +79,7 @@ class DigitalConsoleScreenHandler( } - override fun close( player: PlayerEntity? ) { + override fun close(player: PlayerEntity) { super.close(player); val stack = inventory.getStack(0) @@ -90,11 +91,11 @@ class DigitalConsoleScreenHandler( } - override fun canUse( player: PlayerEntity? ): Boolean { return true } + override fun canUse(player: PlayerEntity): Boolean { return true } - override fun quickMove( player: PlayerEntity?, index: Int ): ItemStack { return ItemStack.EMPTY } + override fun quickMove( player: PlayerEntity, index: Int ): ItemStack { return ItemStack.EMPTY } - override fun onSlotClick( slotIndex: Int, button: Int, actionType: SlotActionType?, player: PlayerEntity? ) { + override fun onSlotClick( slotIndex: Int, button: Int, actionType: SlotActionType, player: PlayerEntity ) { val title = Translation.item("digital_console.select") @@ -110,7 +111,7 @@ class DigitalConsoleScreenHandler( ) - player!!.openHandledScreen(screenFactory) + player.openHandledScreen(screenFactory) } @@ -141,7 +142,7 @@ class DigitalConsoleScreen( private val flatKeyTexture = textureID( path + "flat.png" ) - var recordingFileName = ""; var channel = 1 + var recordingFileName = ""; var channel = 0 var recordCheckbox: CheckboxWidget? = null @@ -185,7 +186,7 @@ class DigitalConsoleScreen( override fun shouldPause(): Boolean { return false } - override fun drawBackground( matrices: MatrixStack?, delta: Float, mouseX: Int, mouseY: Int ) { + override fun drawBackground( matrices: MatrixStack, delta: Float, mouseX: Int, mouseY: Int ) { RenderSystem.setShader( GameRenderer::getPositionTexProgram ) RenderSystem.setShaderColor( 1f, 1f, 1f, 1f ) @@ -334,7 +335,7 @@ class DigitalConsoleScreen( } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices); super.render( matrices, mouseX, mouseY, delta ) @@ -345,7 +346,7 @@ class DigitalConsoleScreen( textRenderer.draw( matrices, octaveTitle, width * 0.5f + 10, height * 0.5f - 65, Color.WHITE.rgb ) - drawTime(matrices!!); recordCheckbox!!.renderButton( matrices, mouseX, mouseY, delta) + drawTime(matrices); recordCheckbox!!.renderButton( matrices, mouseX, mouseY, delta ) } @@ -379,9 +380,7 @@ class DigitalConsoleScreen( private fun drawTime(matrices: MatrixStack) { - if ( !canRecord() ) return - - val tick = sequencer!!.tickPosition + if ( !canRecord() ) return; val tick = sequencer!!.tickPosition val minutes = tick / ( 20 * 60 ); val seconds = ( tick / 20 ) % 60 @@ -425,7 +424,7 @@ class DigitalConsoleScreen( private fun renderKey( textureID: Identifier, keyBinding: KeyBinding, - matrices: MatrixStack?, x: Int, y: Int, w: Int, h: Int + matrices: MatrixStack, x: Int, y: Int, w: Int, h: Int ) { RenderSystem.setShaderTexture( 0, textureID ) @@ -511,13 +510,13 @@ class PickStackScreenHandler( syncID: Int, playerInventory: PlayerInventory ) : } - override fun canUse( player: PlayerEntity? ): Boolean { return true } + override fun canUse(player: PlayerEntity): Boolean { return true } - override fun quickMove( player: PlayerEntity?, index: Int ): ItemStack { return ItemStack.EMPTY } + override fun quickMove( player: PlayerEntity, index: Int ): ItemStack { return ItemStack.EMPTY } - override fun onSlotClick( slotIndex: Int, button: Int, actionType: SlotActionType?, player: PlayerEntity? ) { + override fun onSlotClick( slotIndex: Int, button: Int, actionType: SlotActionType, player: PlayerEntity ) { - if ( slotIndex < 0 || player!!.world.isClient ) return + if ( slotIndex < 0 || player.world.isClient ) return val slotStack = slots[slotIndex].stack; if ( slotStack.item !is Instrument ) return @@ -587,7 +586,7 @@ class PickStackScreen( override fun shouldPause(): Boolean { return false } - override fun drawBackground( matrices: MatrixStack?, delta: Float, mouseX: Int, mouseY: Int ) { + override fun drawBackground( matrices: MatrixStack, delta: Float, mouseX: Int, mouseY: Int ) { RenderSystem.setShader( GameRenderer::getPositionTexProgram ) RenderSystem.setShaderColor( 1f, 1f, 1f, 1f ) @@ -603,7 +602,7 @@ class PickStackScreen( } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices); super.render( matrices, mouseX, mouseY, delta ) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/FloppyDisk.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/FloppyDisk.kt index 2e0d8f3..61f067d 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/FloppyDisk.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/FloppyDisk.kt @@ -37,19 +37,19 @@ class FloppyDisk : Item( defaultSettings().maxDamage( damageSeed() ) ), StackMen override fun trackTick( stack: ItemStack, slot: Int ) { trackPlayer(stack); trackDamage(stack) } - override fun inventoryTick( stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean ) { + override fun inventoryTick( stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { super.inventoryTick( stack, world, entity, slot, selected ) - if ( !world!!.isClient ) return; queryTick(stack!!) + if ( !world.isClient ) return; queryTick(stack) } - override fun use( world: World?, user: PlayerEntity?, hand: Hand? ): TypedActionResult { + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { - val stack = user!!.getStackInHand(hand); val canOpen = canOpenMenu( user, stack ) + val stack = user.getStackInHand(hand); val canOpen = canOpenMenu( user, stack ) - val action = super.use( world, user, hand ); if ( !world!!.isClient || !canOpen ) return action + val action = super.use( world, user, hand ); if ( !world.isClient || !canOpen ) return action client().setScreen( FloppyDiskScreen(stack) ); return action @@ -93,6 +93,8 @@ class FloppyDisk : Item( defaultSettings().maxDamage( damageSeed() ) ), StackMen } + /* TODO: This interrupt query thing keeps requesting twice. + * To replicate set a link, interrupt and spam open a menu. */ /** Queries the source title when requested. */ private fun queryTick(stack: ItemStack) { diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/Screen.kt index 5efccb5..a15253b 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/floppy/Screen.kt @@ -36,6 +36,10 @@ class FloppyDiskScreen( private val stack: ItemStack ) : Screen( Text.of(screenT if ( lastPath == path ) { super.close(); return } + // Reset volume and rate. + + nbt.putFloat( "Rate", 1f ); nbt.putFloat( "Volume", 1f ) + nbt.remove("hasRequestDisplay") if ( path.isNotBlank() ) { @@ -102,7 +106,7 @@ class FloppyDiskScreen( private val stack: ItemStack ) : Screen( Text.of(screenT } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices) @@ -114,11 +118,7 @@ class FloppyDiskScreen( private val stack: ItemStack ) : Screen( Text.of(screenT title = stack.name.string DrawableHelper.drawCenteredText( matrices, textRenderer, title, ( width * 0.5 ).toInt(), 30, 0xFFFFFF ) - textRenderer.draw( - matrices, "$pathTitle:", - searchField!!.x.toFloat(), searchField!!.y.toFloat() - 12, - 0xFFFFFF - ) + textRenderer.draw( matrices, "$pathTitle:", searchField!!.x.toFloat(), searchField!!.y.toFloat() - 12, 0xFFFFFF ) } diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Enchantments.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Enchantments.kt index a7b4827..355a125 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Enchantments.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Enchantments.kt @@ -7,11 +7,9 @@ import net.minecraft.entity.EquipmentSlot import net.minecraft.item.ItemStack class RangedEnchantment : Enchantment( - Rarity.UNCOMMON, EnchantmentTarget.WEAPON, EquipmentSlot.values() + Rarity.UNCOMMON, EnchantmentTarget.WEAPON, EquipmentSlot.entries.toTypedArray() ), ModID { - override fun isAcceptableItem( stack: ItemStack? ): Boolean { - return stack!!.item is Instrument - } + override fun isAcceptableItem(stack: ItemStack): Boolean { return stack.item is Instrument } } \ No newline at end of file diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Instrument.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Instrument.kt index e9fbcbf..4e9d4d9 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Instrument.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Instrument.kt @@ -59,10 +59,7 @@ import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.world.ServerWorld import net.minecraft.sound.SoundCategory import net.minecraft.sound.SoundEvent -import net.minecraft.util.ActionResult -import net.minecraft.util.ClickType -import net.minecraft.util.Hand -import net.minecraft.util.TypedActionResult +import net.minecraft.util.* import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3d import net.minecraft.world.RaycastContext @@ -73,7 +70,7 @@ import kotlin.math.cos import kotlin.math.sin import kotlin.random.Random -val particles = Instrument.Companion.ActionParticles +private val particles = Instrument.Companion.ActionParticles open class Instrument( val damage: Float, val useSpeed: Float, material: ToolMaterial @@ -106,13 +103,13 @@ open class Instrument( override fun trackTick( stack: ItemStack, slot: Int ) { trackHand(stack); trackSlot( stack, slot ) } - override fun inventoryTick( stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean ) { + override fun inventoryTick( stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { super.inventoryTick( stack, world, entity, slot, selected ) - val nbt = NBT.get(stack!!) + val nbt = NBT.get(stack) - if ( !world!!.isClient || entity !is PlayerEntity ) return + if ( !world.isClient || entity !is PlayerEntity ) return val isOnConsole = currentScreen() is DigitalConsoleScreen val shouldStop = nbt.getInt("Hand") == -1 && entity.activeItem != stack @@ -127,19 +124,19 @@ open class Instrument( } override fun onClicked( - stack: ItemStack?, otherStack: ItemStack?, slot: Slot?, clickType: ClickType?, - player: PlayerEntity?, cursorStackReference: StackReference? + stack: ItemStack, otherStack: ItemStack, slot: Slot, clickType: ClickType, + player: PlayerEntity, cursorStackReference: StackReference ): Boolean { - val world = player!!.world + val world = player.world - if ( world.isClient ) { stopSounds(stack!!); stopDeviceSounds(stack) } + if ( world.isClient ) { stopSounds(stack); stopDeviceSounds(stack) } return super.onClicked( stack, otherStack, slot, clickType, player, cursorStackReference ) } - override fun getAttributeModifiers( slot: EquipmentSlot? ): Multimap { + override fun getAttributeModifiers(slot: EquipmentSlot): Multimap { val onMain = slot == EquipmentSlot.MAINHAND @@ -147,25 +144,17 @@ open class Instrument( } - // TODO: Add player pose when playing on use. - override fun use( world: World?, user: PlayerEntity, hand: Hand? ): TypedActionResult? { - - val stack = user.getStackInHand(hand); val nbt = NBT.get(stack) - - val mainStack = user.mainHandStack; val action = TypedActionResult.pass(stack) + override fun getUseAction(stack: ItemStack): UseAction { return UseAction.BOW } - val wasActive = user.activeItem == stack + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { - // Only set the hand when the player has one instrument. - var hasOne = hand == Hand.OFF_HAND && mainStack.item !is Instrument - hasOne = hasOne || hand == Hand.MAIN_HAND + val stack = user.getStackInHand(hand); val nbt = NBT.get(stack) - // Using setCurrentHand() can control the sound length. - if (hasOne) user.setCurrentHand(hand) + val action = TypedActionResult.pass(stack) - if (wasActive) return action; rangedAttack( stack, user ) + if ( !shouldUse( user, stack, hand ) ) return action - if ( !world!!.isClient ) return action + rangedAttack( stack, user ); if ( !world.isClient ) return action val isRanged = nbt.getString("Action") == "Ranged" if ( !isRanged ) particles.clientSpawn( user, "simple" ) @@ -176,13 +165,12 @@ open class Instrument( } - // TODO: Interactive (menu) mobs trigger this a lot, idk why. + // TODO: Interactive (menu) mobs trigger this a lot, I don't know why. override fun useOnEntity( - stack: ItemStack?, player: PlayerEntity?, entity: LivingEntity?, hand: Hand? + stack: ItemStack, player: PlayerEntity, entity: LivingEntity, hand: Hand ): ActionResult { - val player = player!!; val entity = entity!! - val nbt = NBT.get(stack!!); val action = nbt.getString("Action") + val nbt = NBT.get(stack); val action = nbt.getString("Action") val result = ActionResult.PASS // Mute a player. @@ -201,18 +189,17 @@ open class Instrument( } - override fun getMaxUseTime( stack: ItemStack? ): Int { return 200 } + override fun getMaxUseTime(stack: ItemStack): Int { return 200 } override fun onStoppedUsing( - stack: ItemStack?, world: World?, user: LivingEntity?, remainingUseTicks: Int + stack: ItemStack, world: World, user: LivingEntity, remainingUseTicks: Int ) { - val user = user!!; val world = world!! - - if ( !world.isClient || NBT.get(stack!!).getBoolean("onKey") ) return + if ( !world.isClient || NBT.get(stack).getBoolean("onKey") ) return // Stop the off hand stack instrument if there are 2 stacks on hands. val mainStack = user.mainHandStack; val offStack = user.offHandStack + if ( stack == mainStack && offStack.item is Instrument ) { offStack.onStoppedUsing( world, user, remainingUseTicks ) } @@ -658,6 +645,22 @@ open class Instrument( } + fun shouldUse( user: PlayerEntity, stack: ItemStack, hand: Hand ): Boolean { + + val mainStack = user.mainHandStack; val isActive = user.activeItem == stack + + // Only set the hand when the player has one instrument. + var hasOne = hand == Hand.OFF_HAND && mainStack.item !is Instrument + hasOne = hasOne || hand == Hand.MAIN_HAND + + + // Using setCurrentHand() can control the sound length. + if (hasOne) user.setCurrentHand(hand) + + if (isActive) return false; return true + + } + fun mobPlay(mob: MobEntity) { val world = mob.world @@ -1075,6 +1078,7 @@ interface NoFading open class Keyboard : Instrument( 5f, -2.4f, MusicalQuartz() ) class Organ : Instrument( 5f, -3.5f, MusicalIron() ) + open class DrumSet : Instrument( 3.5f, -3f, MusicalIron() ), PlayCompletely open class AcousticGuitar : Instrument( 3f, -2.4f, MusicalString() ) @@ -1083,21 +1087,21 @@ open class ElectricGuitar : Instrument( 4f, -2.4f, MusicalRedstone() ) { private val miningSpeed = MusicalRedstone().miningSpeedMultiplier private val effectiveBlocks = BlockTags.AXE_MINEABLE - override fun getMiningSpeedMultiplier( stack: ItemStack?, state: BlockState? ): Float { + override fun getMiningSpeedMultiplier( stack: ItemStack, state: BlockState ): Float { - return if ( state!!.isIn(effectiveBlocks) ) miningSpeed else 1.0f + return if ( state.isIn(effectiveBlocks) ) miningSpeed else 1.0f } - override fun canMine( state: BlockState?, world: World?, pos: BlockPos?, miner: PlayerEntity? ): Boolean { return true } + override fun canMine( state: BlockState, world: World, pos: BlockPos, miner: PlayerEntity ): Boolean { return true } override fun postMine( - stack: ItemStack?, world: World?, state: BlockState?, pos: BlockPos?, miner: LivingEntity? + stack: ItemStack, world: World, state: BlockState, pos: BlockPos, miner: LivingEntity ): Boolean { - if ( state!!.isIn(effectiveBlocks) ) { + if ( state.isIn(effectiveBlocks) ) { - stack!!.damage( 1, miner ) { breakEquipment( miner, stack ) } + stack.damage( 1, miner ) { breakEquipment( miner, stack ) } } @@ -1126,26 +1130,31 @@ class ElectricGuitarClean : ElectricGuitar() { } - override fun use( world: World?, user: PlayerEntity, hand: Hand? ): TypedActionResult? { + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { + + val stack = user.getStackInHand(hand); val action = TypedActionResult.pass(stack) + + if ( user.isOnFire && user.isSneaking ) { - val stack = user.getStackInHand(hand) + if ( !shouldUse( user, stack, hand ) ) return action - if ( user.isOnFire && user.isSneaking ) ability(stack, user, user) - else super.use( world, user, hand ) + ability( stack, user, user ) - return TypedActionResult.pass(stack) + } else super.use( world, user, hand ) + + return action } override fun useOnEntity( - stack: ItemStack?, player: PlayerEntity?, entity: LivingEntity?, hand: Hand? + stack: ItemStack, player: PlayerEntity, entity: LivingEntity, hand: Hand ): ActionResult { - val canHelp = entity!!.wasOnFire && entity !is HostileEntity + val canHelp = entity.wasOnFire && entity !is HostileEntity return if (canHelp) { - ability( stack!!, player!!, entity ); ActionResult.CONSUME + ability( stack, player, entity ); ActionResult.CONSUME } else super.useOnEntity( stack, player, entity, hand ) @@ -1154,14 +1163,15 @@ class ElectricGuitarClean : ElectricGuitar() { } class Harp : Instrument( 2f, -1f, MusicalString() ) -open class Viola : Instrument( 3.5f, -2f, MusicalString() ) -open class Violin : Instrument( 3.75f, -2f, MusicalRedstone() ) class Recorder : Instrument( 1.25f, -1.5f, MusicalString() ) class Oboe : Instrument( 3.25f, -1f, MusicalIron() ) +open class Viola : Instrument( 3.5f, -2f, MusicalString() ) +open class Violin : Instrument( 3.75f, -2f, MusicalRedstone() ) + open class Trombone : Instrument( 5f, -3f, MusicalRedstone() ) { - override fun use( world: World?, user: PlayerEntity, hand: Hand? ): TypedActionResult? { + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { val result = super.use( world, user, hand ); val stack = user.getStackInHand(hand) @@ -1169,7 +1179,7 @@ open class Trombone : Instrument( 5f, -3f, MusicalRedstone() ) { val rotation = user.rotationVector.multiply(4.0); val pos = user.eyePos - val raycast = world!!.raycast( RaycastContext( pos, pos.add(rotation), RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, user ) ) + val raycast = world.raycast( RaycastContext( pos, pos.add(rotation), RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, user ) ) var canThrust = action != "Thrust" || !user.isOnGround canThrust = canThrust || world.getBlockState( raycast.blockPos ).block is AirBlock @@ -1206,11 +1216,11 @@ class Rhodes : Keyboard() abstract class Synth : Keyboard() -class BassSynth : Synth(); class BassLeadSynth : Synth() -class Bass2Synth : Synth(); class CelesteSynth : Synth() -class DocSynth : Synth(); class MetalPadSynth : Synth() -class PolySynth : Synth(); class SawSynth : Synth() -class SineSynth : Synth(); class SquareSynth : Synth() +class BassSynth : Synth(); class BassLeadSynth : Synth() +class Bass2Synth : Synth(); class CelesteSynth : Synth() +class DocSynth : Synth(); class MetalPadSynth : Synth() +class PolySynth : Synth(); class SawSynth : Synth() +class SineSynth : Synth(); class SquareSynth : Synth() class StringsSynth : Synth() class Banjo : AcousticGuitar(); class Cello : Instrument( 2f, -2f, MusicalString() ) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Receiver.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Receiver.kt index 685662f..6230bed 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Receiver.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Receiver.kt @@ -12,6 +12,8 @@ import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.util.collection.DefaultedList +private val particles = Instrument.Companion.ActionParticles + @Environment(EnvType.CLIENT) class InstrumentReceiver( private val deviceID: String ) : GenericReceiver() { diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Screen.kt index a9f4281..928eb76 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/instruments/Screen.kt @@ -253,7 +253,7 @@ class InstrumentsScreen( private val stack: ItemStack ) : Screen( Text.of("Instr } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices) @@ -266,24 +266,14 @@ class InstrumentsScreen( private val stack: ItemStack ) : Screen( Text.of("Instr } val title = stack.name.string - textRenderer.draw( matrices, title, width - title.length * 5f - 20, - 10f, 0xFFFFFF - ) + textRenderer.draw( matrices, title, width - title.length * 5f - 20, 10f, 0xFFFFFF ) - textRenderer.draw( - matrices, "$sequenceTitle:", - sequenceField!!.x.toFloat(), sequenceField!!.y.toFloat() - 12, - 0xFFFFFF - ) + textRenderer.draw( matrices, "$sequenceTitle:", sequenceField!!.x.toFloat(), sequenceField!!.y.toFloat() - 12, 0xFFFFFF ) - textRenderer.draw( - matrices, "$channelTitle: ", - channelField!!.x.toFloat() - 45, channelField!!.y.toFloat() + 5, - 0xFFFFFF - ) + textRenderer.draw( matrices, "$channelTitle: ", channelField!!.x.toFloat() - 45, channelField!!.y.toFloat() + 5, 0xFFFFFF ) // Show device name on change using a "tween". - deviceNameDraw( matrices ) + deviceNameDraw(matrices) } @@ -315,7 +305,7 @@ class InstrumentsScreen( private val stack: ItemStack ) : Screen( Text.of("Instr private fun resetTween() { tweenStack.replaceAll { 0 } } - private fun deviceNameDraw( matrices: MatrixStack? ) { + private fun deviceNameDraw(matrices: MatrixStack) { val t = tweenStack diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/storage/MusicalStorage.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/storage/MusicalStorage.kt index 8db0f53..e94ca2e 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/storage/MusicalStorage.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/storage/MusicalStorage.kt @@ -1,8 +1,8 @@ package com.enginemachiner.honkytones.items.storage import com.enginemachiner.honkytones.* -import com.enginemachiner.honkytones.mixins.chest.ChestBlockEntityAccessor -import com.enginemachiner.honkytones.mixins.chest.LidAnimatorAccessor +import com.enginemachiner.honkytones.mixin.chest.ChestBlockEntityAccessor +import com.enginemachiner.honkytones.mixin.chest.LidAnimatorAccessor import net.fabricmc.api.EnvType import net.fabricmc.api.Environment import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking @@ -49,24 +49,24 @@ class MusicalStorage : Item( defaultSettings() ), StackMenu { } override fun inventoryTick( - stack: ItemStack?, world: World?, entity: Entity?, slot: Int, selected: Boolean + stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean ) { super.inventoryTick( stack, world, entity, slot, selected ) - createModels(stack!!) + createModels(stack) } - override fun use( world: World?, user: PlayerEntity?, hand: Hand? ): TypedActionResult { + override fun use( world: World, user: PlayerEntity, hand: Hand ): TypedActionResult { - val stack = user!!.getStackInHand(hand) + val stack = user.getStackInHand(hand) val canOpen = canOpenMenu( user, stack ) val action = TypedActionResult.consume(stack) - if ( world!!.isClient ) return TypedActionResult.pass(stack) + if ( world.isClient ) return TypedActionResult.pass(stack) if ( !canOpen ) return action @@ -278,9 +278,7 @@ class MusicalStorage : Item( defaultSettings() ), StackMenu { createModels(stack) - val user = stack.holder!! as PlayerEntity - - val world = user.world + val user = stack.holder!! as PlayerEntity; val world = user.world val nbt = NBT.get(stack); val stackID = nbt.getInt("ID") diff --git a/src/main/kotlin/com/enginemachiner/honkytones/items/storage/Screen.kt b/src/main/kotlin/com/enginemachiner/honkytones/items/storage/Screen.kt index 5a27d6b..e78b578 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/items/storage/Screen.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/items/storage/Screen.kt @@ -85,15 +85,15 @@ class StorageScreenHandler( } - override fun close( player: PlayerEntity? ) { + override fun close(player: PlayerEntity) { super.close(player); storage.close(stack); inventory.markDirty() } - override fun canUse( player: PlayerEntity? ): Boolean { return inventory.canPlayerUse(player) } + override fun canUse(player: PlayerEntity): Boolean { return inventory.canPlayerUse(player) } - override fun quickMove( player: PlayerEntity?, index: Int ): ItemStack { + override fun quickMove( player: PlayerEntity, index: Int ): ItemStack { var currentStack = ItemStack.EMPTY; val currentSlot = slots[index] @@ -116,7 +116,7 @@ class StorageScreenHandler( } override fun onSlotClick( - slotIndex: Int, button: Int, actionType: SlotActionType?, player: PlayerEntity? + slotIndex: Int, button: Int, actionType: SlotActionType, player: PlayerEntity ) { // THROW action. @@ -161,7 +161,7 @@ class StorageScreen( init { backgroundHeight += 60; playerInventoryTitleY += 55; titleX += 40 } - override fun drawBackground( matrices: MatrixStack?, delta: Float, mouseX: Int, mouseY: Int ) { + override fun drawBackground( matrices: MatrixStack, delta: Float, mouseX: Int, mouseY: Int ) { RenderSystem.setShader( GameRenderer::getPositionTexProgram ) RenderSystem.setShaderColor( 1f, 1f, 1f, 1f ) @@ -194,7 +194,7 @@ class StorageScreen( } - override fun render( matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float ) { + override fun render( matrices: MatrixStack, mouseX: Int, mouseY: Int, delta: Float ) { renderBackground(matrices); super.render( matrices, mouseX, mouseY, delta ) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/sound/AudioStreaming.kt b/src/main/kotlin/com/enginemachiner/honkytones/sound/AudioStreaming.kt index e42a26e..73a07a4 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/sound/AudioStreaming.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/sound/AudioStreaming.kt @@ -2,7 +2,7 @@ package com.enginemachiner.honkytones.sound import MarkErrorInputStream import com.enginemachiner.honkytones.* import com.enginemachiner.honkytones.CanBeMuted.Companion.isMuted -import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerBlockEntity +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayer import com.squareup.okhttp.OkHttpClient import com.squareup.okhttp.Request import net.fabricmc.api.EnvType @@ -97,21 +97,15 @@ private class DirectAudioStream( private val sound: ExternalSound ) : AudioStrea /** Sound that can stream or play certain external audio files. */ @Environment(EnvType.CLIENT) -class ExternalSound( +class ExternalSound( private val musicPlayer: MusicPlayer ) : FadingSound("audio_stream") { - val sourceInput: String, - private val musicPlayer: MusicPlayerBlockEntity + val sourceInput = musicPlayer.path; init { init() } -) : FadingSound("audio_stream") { - - private var nbtVolume = 1f - private var audioStream: AudioStream? = null - - init { init() } + private var nbtVolume = 1f; private var audioStream: AudioStream? = null private fun init() { - entity = musicPlayer.entity + entity = musicPlayer.blockEntity!!.entity try { audioStream = getAudioStream() } catch (e: Exception) { @@ -123,6 +117,8 @@ class ExternalSound( } + fun isValid(): Boolean { return audioStream != null } + private fun getAudioStream(): AudioStream { return if ( sourceInput.endsWith(".ogg") ) { @@ -139,9 +135,9 @@ class ExternalSound( private fun setNBTVolume() { - val floppyStack = musicPlayer.getStack(16) + val floppy = musicPlayer.items[16] - if ( floppyStack.isEmpty ) return; val nbt = NBT.get(floppyStack) + if ( floppy.isEmpty ) return; val nbt = NBT.get(floppy) if ( isMuted(entity!!) ) { nbtVolume = 0f; return }; val volume = nbt.getFloat("Volume") @@ -149,17 +145,19 @@ class ExternalSound( } + override fun fadeOut() { if ( !musicPlayer.blockEntity!!.repeatOnPlay ) super.fadeOut() else stop() } + override fun tick() { super.tick(); if ( isStopping() ) return; setNBTVolume() val factor = Sound.MIN_DISTANCE.pow(2) * 0.5f - val distance1 = musicPlayer.pos.getSquaredDistance( player()!!.pos ) * 0.05 + val distance1 = musicPlayer.pos().getSquaredDistance( player()!!.pos ) * 0.03 var distance2 = factor - distance1; distance2 /= factor - distance2 += 0.06f; distance2 *= nbtVolume + distance2 += 0.05f; distance2 *= nbtVolume volume = max( 0f, min( 1f, distance2.toFloat() ) ) @@ -167,15 +165,11 @@ class ExternalSound( public override fun stop() { - if ( !isPlaying() ) return; modPrint("$entity: Stopped.") - - musicPlayer.spawnParticles = false; musicPlayer.setPlayingState(false) - - super.stop() + if ( !isPlaying() ) return; super.stop(); musicPlayer.pause() } - override fun getAudioStream( loader: SoundLoader?, id: Identifier?, shouldLoop: Boolean ): CompletableFuture { + override fun getAudioStream( loader: SoundLoader, id: Identifier, shouldLoop: Boolean ): CompletableFuture { if ( audioStream == null ) return super.getAudioStream( loader, id, shouldLoop ) diff --git a/src/main/kotlin/com/enginemachiner/honkytones/sound/InstrumentSound.kt b/src/main/kotlin/com/enginemachiner/honkytones/sound/InstrumentSound.kt index 7314ae7..fbafe01 100644 --- a/src/main/kotlin/com/enginemachiner/honkytones/sound/InstrumentSound.kt +++ b/src/main/kotlin/com/enginemachiner/honkytones/sound/InstrumentSound.kt @@ -2,6 +2,7 @@ package com.enginemachiner.honkytones.sound import com.enginemachiner.honkytones.* import com.enginemachiner.honkytones.CanBeMuted.Companion.isMuted +import com.enginemachiner.honkytones.blocks.musicplayer.MusicPlayerEntity import com.enginemachiner.honkytones.items.instruments.Instrument import com.enginemachiner.honkytones.items.instruments.NoFading import com.enginemachiner.honkytones.items.instruments.PlayCompletely @@ -45,7 +46,9 @@ open class InstrumentSound(path: String) : StackSound(path) { override fun playOnClients() { - val netId = InstrumentSoundNetworking.netID("play") + var netID = InstrumentSoundNetworking.netID("play") + + if ( entity is MusicPlayerEntity ) netID = InstrumentSoundNetworking.netID("play_on_player") val buf = PacketByteBufs.create() @@ -53,20 +56,22 @@ open class InstrumentSound(path: String) : StackSound(path) { buf.writeFloat(maxVolume); buf.writeInt(semitones) buf.writeInt( entity!!.id ) - ClientPlayNetworking.send( netId, buf ) + ClientPlayNetworking.send( netID, buf ) } override fun fadeOutOnClients() { - val netId = InstrumentSoundNetworking.netID("stop") + var netID = InstrumentSoundNetworking.netID("stop") + + if ( entity is MusicPlayerEntity ) netID = InstrumentSoundNetworking.netID("stop_on_player") val buf = PacketByteBufs.create() buf.writeString(path); buf.writeItemStack(stack) buf.writeInt(semitones) - ClientPlayNetworking.send( netId, buf ) + ClientPlayNetworking.send( netID, buf ) } @@ -87,6 +92,8 @@ class NoteProjectileSound( path: String, pos: Vec3d, semitones: Int ) : Instrume object InstrumentSoundNetworking : ModID { + private fun playerFilter( current: ServerPlayerEntity, sender: ServerPlayerEntity ): Boolean { return current != sender } + private fun commonFilter( addDistance: Double, current: ServerPlayerEntity, sender: ServerPlayerEntity ): Boolean { return current.blockPos.isWithinDistance( sender.pos, Sound.MIN_DISTANCE + addDistance ) && current != sender } @@ -119,110 +126,105 @@ object InstrumentSoundNetworking : ModID { fun networking() { - registerSpecialServerReceiver( - - netID("play"), - - { - - sentBuf: PacketByteBuf, nextBuf: PacketByteBuf -> - - nextBuf.writeString( sentBuf.readString() ); nextBuf.writeItemStack( sentBuf.readItemStack() ) - nextBuf.writeFloat( sentBuf.readFloat() ); nextBuf.writeInt( sentBuf.readInt() ) - nextBuf.writeInt( sentBuf.readInt() ) + fun writePlayBuf( sentBuf: PacketByteBuf, nextBuf: PacketByteBuf ) { - }, + nextBuf.writeString( sentBuf.readString() ); nextBuf.writeItemStack( sentBuf.readItemStack() ) + nextBuf.writeFloat( sentBuf.readFloat() ); nextBuf.writeInt( sentBuf.readInt() ) + nextBuf.writeInt( sentBuf.readInt() ) - ::playFilter + } - ) + fun writeStopBuf( sentBuf: PacketByteBuf, nextBuf: PacketByteBuf ) { - registerSpecialServerReceiver( + nextBuf.writeString( sentBuf.readString() ); nextBuf.writeItemStack( sentBuf.readItemStack() ) + nextBuf.writeInt( sentBuf.readInt() ) - netID("stop"), + } - { + registerSpecialServerReceiver( netID("play"), ::writePlayBuf, ::playFilter ) + registerSpecialServerReceiver( netID("stop"), ::writeStopBuf, ::fadeOutFilter ) - sentBuf: PacketByteBuf, nextBuf: PacketByteBuf -> + registerSpecialServerReceiver( netID("play_on_player"), ::writePlayBuf, ::playerFilter ) + registerSpecialServerReceiver( netID("stop_on_player"), ::writeStopBuf, ::playerFilter ) - nextBuf.writeString( sentBuf.readString() ); nextBuf.writeItemStack( sentBuf.readItemStack() ) - nextBuf.writeInt( sentBuf.readInt() ) + if ( !isClient() ) return - }, + for ( netName in listOf( "play", "play_on_player" ) ) { - ::fadeOutFilter + val id = netID(netName) - ) + ClientPlayNetworking.registerGlobalReceiver(id) { - if ( !isClient() ) return + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - var id = netID("play") - ClientPlayNetworking.registerGlobalReceiver(id) { + val path = buf.readString(); val netStack = buf.readItemStack() + val maxVolume = buf.readFloat(); val semitones = buf.readInt() + val id = buf.readInt() - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + /* - val path = buf.readString(); val netStack = buf.readItemStack() - val maxVolume = buf.readFloat(); val semitones = buf.readInt() - val id = buf.readInt() + Using the netStack to play the sounds is wrong because each time + there is a new stack instance / object that would try to get + and create stack sounds, wasting resources. To avoid this I'll store + them and search them by an NBT ID, so they can be reused. - /* + */ - Using the netStack to play the sounds is wrong because each time - there is a new stack instance / object that would try to get - and create stack sounds, wasting resources. To avoid this I'll store - them and search them by an NBT ID, so they can be reused. + client.send { - */ + val holder = entity(id) - client.send { + if ( holder == null || isMuted(holder) ) return@send - val stack = findStack(netStack) + val stack = findStack(netStack) - val instrument = stack.item as Instrument + val instrument = stack.item as Instrument - val notes = instrument.stackSounds(stack).notes - val device = instrument.stackSounds(stack).deviceNotes + val notes = instrument.stackSounds(stack).notes + val device = instrument.stackSounds(stack).deviceNotes - var sound = findSound( notes, path, semitones ) + var sound = findSound( notes, path, semitones ) - if ( sound.isPlaying() ) sound = findSound( device, path, semitones ) + if ( sound.isPlaying() ) sound = findSound( device, path, semitones ) - val holder = entity(id)!!; stack.holder = holder + stack.holder = holder; sound.shouldNetwork = false - if ( isMuted(holder) ) return@send + sound.maxVolume = maxVolume; sound.play(stack) - sound.shouldNetwork = false; sound.maxVolume = maxVolume - - sound.play(stack) + } } } - id = netID("stop") - ClientPlayNetworking.registerGlobalReceiver(id) { + for ( netName in listOf( "stop", "stop_on_player" ) ) { + + val id = netID(netName) + ClientPlayNetworking.registerGlobalReceiver(id) { + + client: MinecraftClient, _: ClientPlayNetworkHandler, + buf: PacketByteBuf, _: PacketSender -> - client: MinecraftClient, _: ClientPlayNetworkHandler, - buf: PacketByteBuf, _: PacketSender -> + val path = buf.readString(); val netStack = buf.readItemStack() + val semitones = buf.readInt() - val path = buf.readString(); val netStack = buf.readItemStack() - val semitones = buf.readInt() + client.send { - client.send { + val stack = findStack(netStack) - val stack = findStack(netStack) + val instrument = stack.item as Instrument - val instrument = stack.item as Instrument + val notes = instrument.stackSounds(stack).notes + val device = instrument.stackSounds(stack).deviceNotes - val notes = instrument.stackSounds(stack).notes - val device = instrument.stackSounds(stack).deviceNotes + var sound = findSound( notes, path, semitones ) - var sound = findSound( notes, path, semitones ) + if ( sound.isStopping() ) sound = findSound( device, path, semitones ) - if ( sound.isStopping() ) sound = findSound( device, path, semitones ) + if ( !sound.isPlaying() ) return@send; sound.fadeOut() - if ( !sound.isPlaying() ) return@send; sound.fadeOut() + } } diff --git a/src/main/resources/assets/honkytones/mod-icon.png b/src/main/resources/assets/honkytones/icon.png similarity index 100% rename from src/main/resources/assets/honkytones/mod-icon.png rename to src/main/resources/assets/honkytones/icon.png diff --git a/src/main/resources/assets/honkytones/lang/en_us.json b/src/main/resources/assets/honkytones/lang/en_us.json index 9263b3a..1b41bc7 100644 --- a/src/main/resources/assets/honkytones/lang/en_us.json +++ b/src/main/resources/assets/honkytones/lang/en_us.json @@ -53,7 +53,8 @@ "block.honkytones.music_player": "Music Player", "block.honkytones.music_player.rate": "Tempo BPM", - "block.honkytones.music_player.downloads": "Sync downloads", + "block.honkytones.music_player.listen": "Listen", + "block.honkytones.music_player.repeat": "Repeat Mode", "item.honkytones.digital_console": "Digital Console", "category.honkytones.digital_console": "HonkyTones Digital Console", @@ -145,7 +146,7 @@ "honkytones.help.keep_downloads": "Keep downloads permanently when requesting web content.", "honkytones.help.keep_videos": "Keep videos when requesting web content.", "honkytones.help.audio_quality": "Choose from 0 to 10.", - "honkytones.help.sync_all": "Sync with all the music players.", + "honkytones.help.listen_all": "Listen to all the music players.", "honkytones.help.max_length": "Max seconds for a youtube-dl request.", "honkytones.help.mobs_playing_delay": "Change the delay of mobs playing (Has to be > 120).", diff --git a/src/main/resources/assets/honkytones/lang/es_ec.json b/src/main/resources/assets/honkytones/lang/es_ec.json index 090411f..0de6033 100644 --- a/src/main/resources/assets/honkytones/lang/es_ec.json +++ b/src/main/resources/assets/honkytones/lang/es_ec.json @@ -53,7 +53,8 @@ "block.honkytones.music_player": "Reproductor", "block.honkytones.music_player.rate": "Tempo PPM", - "block.honkytones.music_player.downloads": "Sincronizar", + "block.honkytones.music_player.listen": "Escuchar", + "block.honkytones.music_player.repeat": "Modo Repetición", "item.honkytones.digital_console": "Consola digital", "category.honkytones.digital_console": "Consola digital HonkyTones", @@ -68,21 +69,21 @@ "item.honkytones.gui.file.channel": "Canal MIDI", "item.honkytones.gui.file.overwrite": "¿sobrescribir?", - "key.honkytones.octave-up": "Subir una octava", - "key.honkytones.octave-down": "Bajar una octava", - - "key.honkytones.play-c": "Tocar nota C", - "key.honkytones.play-d-flat": "Tocar nota D♭", - "key.honkytones.play-d": "Tocar nota D", - "key.honkytones.play-e-flat": "Tocar nota E♭", - "key.honkytones.play-e": "Tocar nota E", - "key.honkytones.play-f": "Tocar nota F", - "key.honkytones.play-g-flat": "Tocar nota G♭", - "key.honkytones.play-g": "Tocar nota G", - "key.honkytones.play-a-flat": "Tocar nota A♭", - "key.honkytones.play-a": "Tocar nota A", - "key.honkytones.play-b-flat": "Tocar nota B♭", - "key.honkytones.play-b": "Tocar nota B", + "key.honkytones.octave_up": "Subir una octava", + "key.honkytones.octave_down": "Bajar una octava", + + "key.honkytones.play_c": "Tocar nota C", + "key.honkytones.play_d_flat": "Tocar nota D♭", + "key.honkytones.play_d": "Tocar nota D", + "key.honkytones.play_e_flat": "Tocar nota E♭", + "key.honkytones.play_e": "Tocar nota E", + "key.honkytones.play_f": "Tocar nota F", + "key.honkytones.play_g_flat": "Tocar nota G♭", + "key.honkytones.play_g": "Tocar nota G", + "key.honkytones.play_a_flat": "Tocar nota A♭", + "key.honkytones.play_a": "Tocar nota A", + "key.honkytones.play_b_flat": "Tocar nota B♭", + "key.honkytones.play_b": "Tocar nota B", "category.honkytones.instrument": "Instrumento HonkyTones", @@ -145,7 +146,7 @@ "honkytones.help.keep_downloads": "Almacenar permanente los contenidos de las peticiones.", "honkytones.help.keep_videos": "Almacenar videos.", "honkytones.help.audio_quality": "Calidad de audio de 0 a 10.", - "honkytones.help.sync_all": "Sincronizar con todos los reproductores.", + "honkytones.help.listen_all": "Escuchar a todos los reproductores.", "honkytones.help.max_length": "Segundos limite para una petición youtube-dl.", "honkytones.help.mobs_playing_delay": "Cambiar el intervalo de los sonidos de las creaturas (Tiene que ser mayor a 120).", diff --git a/src/main/resources/assets/honkytones/textures/item/item_group.png b/src/main/resources/assets/honkytones/textures/item/item_group.png index ec2afa7..ef5fb89 100644 Binary files a/src/main/resources/assets/honkytones/textures/item/item_group.png and b/src/main/resources/assets/honkytones/textures/item/item_group.png differ diff --git a/src/main/resources/assets/honkytones/textures/item/string/guitar/electric-clean.png b/src/main/resources/assets/honkytones/textures/item/string/guitar/electric-clean.png index 578df90..d8b63e9 100644 Binary files a/src/main/resources/assets/honkytones/textures/item/string/guitar/electric-clean.png and b/src/main/resources/assets/honkytones/textures/item/string/guitar/electric-clean.png differ diff --git a/src/main/resources/assets/honkytones/textures/item/string/guitar/electric.png b/src/main/resources/assets/honkytones/textures/item/string/guitar/electric.png index 9eb6828..8e170d7 100644 Binary files a/src/main/resources/assets/honkytones/textures/item/string/guitar/electric.png and b/src/main/resources/assets/honkytones/textures/item/string/guitar/electric.png differ diff --git a/src/main/resources/assets/honkytones/textures/item/string/harp.png b/src/main/resources/assets/honkytones/textures/item/string/harp.png index c488cb6..12910f6 100644 Binary files a/src/main/resources/assets/honkytones/textures/item/string/harp.png and b/src/main/resources/assets/honkytones/textures/item/string/harp.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 14ca432..0489b01 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,61 +1,53 @@ { - "schemaVersion": 1, - "id": "honkytones", - "version": "4.0.0", - "name": "HonkyTones", - "description": "Mod that reads MIDI stuff and does other things.", - - "authors": [ "Engine_Machiner" ], - - "contact": { - "homepage": "https://modrinth.com/mod/honkytones", - "issues": "https://github.com/EngineMachiner/HonkyTones/issues", - "sources": "https://github.com/EngineMachiner/HonkyTones" - }, - - "license": "GNU Lesser General Public License version 3 (LGPLv3)", - "icon": "assets/honkytones/mod-icon.png", - "environment": "*", - - "entrypoints": { - - "main": [ - - { - "adapter": "kotlin", - "value": "com.enginemachiner.honkytones.Init" - }, - - { - "adapter": "kotlin", - "value": "com.enginemachiner.honkytones.Particles" - } - - ], - - "client": [ - "com.enginemachiner.honkytones.Init", - "com.enginemachiner.honkytones.Particles", - "com.enginemachiner.honkytones.Projectiles" - ] - - }, - - "depends": { - "fabricloader": ">=0.11.7", - "fabric": "*", - "fabric-language-kotlin": ">=1.7.4+kotlin.1.6.21", - "minecraft": "1.19.x", - "java": ">=16" - }, - - "mixins": [ - "honkytones.mixins.json", - "honkytones.mixins.mob.json", - "honkytones.mixins.chest.json", - "honkytones.mixins.player.json", - "honkytones.mixins.enchantments.json" - ] - -} + "schemaVersion": 1, + + "id": "honkytones", "version": "${version}", "name": "HonkyTones", + "description": "It adds a very musical experience!", + "license": "GNU Lesser General Public License version 3 (LGPLv3)", + "icon": "assets/honkytones/icon.png", + "authors": ["Engine_Machiner"], + + "contact": { + "homepage": "https://modrinth.com/mod/honkytones", + "issues": "https://github.com/EngineMachiner/HonkyTones/issues", + "sources": "https://github.com/EngineMachiner/HonkyTones" + }, + + "environment": "*", + "entrypoints": { + + "main": [ + "com.enginemachiner.honkytones.Init", + "com.enginemachiner.honkytones.Particles" + ], + + "client": [ + "com.enginemachiner.honkytones.Init", + "com.enginemachiner.honkytones.Particles", + "com.enginemachiner.honkytones.Projectiles" + ], + + "server": [ "com.enginemachiner.honkytones.Commands" ] + + }, + + "mixins": [ + "honkytones.mixins.json", + "honkytones.mixins.mob.json", + "honkytones.mixins.chest.json", + "honkytones.mixins.player.json", + "honkytones.mixins.enchantments.json" + ], + + "depends": { + "fabricloader": ">=0.14.22", + "minecraft": "~1.19.2", + "java": ">=17", + "fabric-api": "*", + "fabric-language-kotlin": ">=1.9.10" + }, + + "suggests": { "another-mod": "*" } + +} \ No newline at end of file diff --git a/src/main/resources/honkytones.mixins.chest.json b/src/main/resources/honkytones.mixins.chest.json index 8028bf9..794f0a6 100644 --- a/src/main/resources/honkytones.mixins.chest.json +++ b/src/main/resources/honkytones.mixins.chest.json @@ -1,7 +1,7 @@ { "required": true, - "package": "com.enginemachiner.honkytones.mixins.chest", - "compatibilityLevel": "JAVA_16", + "package": "com.enginemachiner.honkytones.mixin.chest", + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 }, "mixins": [ "ChestBlockEntityAccessor", "LidAnimatorAccessor" ] } \ No newline at end of file diff --git a/src/main/resources/honkytones.mixins.enchantments.json b/src/main/resources/honkytones.mixins.enchantments.json index bf61704..ba650e2 100644 --- a/src/main/resources/honkytones.mixins.enchantments.json +++ b/src/main/resources/honkytones.mixins.enchantments.json @@ -1,7 +1,7 @@ { "required": true, - "package": "com.enginemachiner.honkytones.mixins.enchantments", - "compatibilityLevel": "JAVA_16", + "package": "com.enginemachiner.honkytones.mixin.enchantments", + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 }, "mixins": [ "EnchantmentMixin", "EnchantmentHelperMixin" ] } \ No newline at end of file diff --git a/src/main/resources/honkytones.mixins.json b/src/main/resources/honkytones.mixins.json index ce4dc51..2f13e7e 100644 --- a/src/main/resources/honkytones.mixins.json +++ b/src/main/resources/honkytones.mixins.json @@ -1,7 +1,8 @@ { "required": true, - "package": "com.enginemachiner.honkytones.mixins", - "compatibilityLevel": "JAVA_16", + "package": "com.enginemachiner.honkytones.mixin", + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 }, - "mixins": ["CrashReportMixin"] + "mixins": ["CrashReportMixin"], + "client": ["ClientWorldMixin"] } \ No newline at end of file diff --git a/src/main/resources/honkytones.mixins.mob.json b/src/main/resources/honkytones.mixins.mob.json index 4603599..fd1fd41 100644 --- a/src/main/resources/honkytones.mixins.mob.json +++ b/src/main/resources/honkytones.mixins.mob.json @@ -1,7 +1,7 @@ { "required": true, - "package": "com.enginemachiner.honkytones.mixins.mob", - "compatibilityLevel": "JAVA_16", + "package": "com.enginemachiner.honkytones.mixin.mob", + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 }, "mixins": [ "MobsCanPlay", "MenuMob", "MobEntityMixin" ] } \ No newline at end of file diff --git a/src/main/resources/honkytones.mixins.player.json b/src/main/resources/honkytones.mixins.player.json index 267b138..d7f61ff 100644 --- a/src/main/resources/honkytones.mixins.player.json +++ b/src/main/resources/honkytones.mixins.player.json @@ -1,7 +1,7 @@ { "required": true, - "package": "com.enginemachiner.honkytones.mixins.player", - "compatibilityLevel": "JAVA_16", + "package": "com.enginemachiner.honkytones.mixin.player", + "compatibilityLevel": "JAVA_17", "injectors": { "defaultRequire": 1 }, "mixins": ["PlayerEntityMixin"], "client": [ "ClientPlayerMixin", "FloppyOnScreen" ]