Browse Source

first commit

master
asie 1 month ago
commit
d99c28678b
100 changed files with 8796 additions and 0 deletions
  1. 54
    0
      build.gradle
  2. 3
    0
      gradle.properties
  3. BIN
      gradle/wrapper/gradle-wrapper.jar
  4. 6
    0
      gradle/wrapper/gradle-wrapper.properties
  5. 164
    0
      gradlew
  6. 90
    0
      gradlew.bat
  7. 31
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentContext.java
  8. 24
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentEvent.java
  9. 90
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentEventHandler.java
  10. 37
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentEventListener.java
  11. 38
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentInterface.java
  12. 51
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentMethod.java
  13. 46
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentRule.java
  14. 35
    0
      src/main/java/org/terasology/kallisti/base/component/ComponentTickEvent.java
  15. 623
    0
      src/main/java/org/terasology/kallisti/base/component/Machine.java
  16. 29
    0
      src/main/java/org/terasology/kallisti/base/component/MachineInvalidStateException.java
  17. 29
    0
      src/main/java/org/terasology/kallisti/base/component/Peripheral.java
  18. 31
    0
      src/main/java/org/terasology/kallisti/base/interfaces/ConnectedContext.java
  19. 214
    0
      src/main/java/org/terasology/kallisti/base/interfaces/FileSystem.java
  20. 44
    0
      src/main/java/org/terasology/kallisti/base/interfaces/FrameBuffer.java
  21. 27
    0
      src/main/java/org/terasology/kallisti/base/interfaces/Identifiable.java
  22. 75
    0
      src/main/java/org/terasology/kallisti/base/interfaces/KeyboardInputProvider.java
  23. 26
    0
      src/main/java/org/terasology/kallisti/base/interfaces/Labelable.java
  24. 33
    0
      src/main/java/org/terasology/kallisti/base/interfaces/Persistable.java
  25. 49
    0
      src/main/java/org/terasology/kallisti/base/interfaces/StaticByteStorage.java
  26. 65
    0
      src/main/java/org/terasology/kallisti/base/interfaces/Synchronizable.java
  27. 29
    0
      src/main/java/org/terasology/kallisti/base/proxy/Proxy.java
  28. 22
    0
      src/main/java/org/terasology/kallisti/base/proxy/ProxyFactory.java
  29. 103
    0
      src/main/java/org/terasology/kallisti/base/util/CollectionBackedMultiValueMap.java
  30. 62
    0
      src/main/java/org/terasology/kallisti/base/util/Dimension.java
  31. 67
    0
      src/main/java/org/terasology/kallisti/base/util/KallistiArgUtils.java
  32. 47
    0
      src/main/java/org/terasology/kallisti/base/util/KallistiColor.java
  33. 75
    0
      src/main/java/org/terasology/kallisti/base/util/KallistiFileUtils.java
  34. 73
    0
      src/main/java/org/terasology/kallisti/base/util/KallistiMath.java
  35. 87
    0
      src/main/java/org/terasology/kallisti/base/util/KallistiReflect.java
  36. 39
    0
      src/main/java/org/terasology/kallisti/base/util/MultiValueMap.java
  37. 33
    0
      src/main/java/org/terasology/kallisti/base/util/PersistenceException.java
  38. 62
    0
      src/main/java/org/terasology/kallisti/base/util/PixelDimension.java
  39. 48
    0
      src/main/java/org/terasology/kallisti/base/util/SimpleFrameBufferImage.java
  40. 136
    0
      src/main/java/org/terasology/kallisti/base/util/keyboard/TranslationAWTLWJGL.java
  41. 109
    0
      src/main/java/org/terasology/kallisti/jnlua/KallistiConverter.java
  42. 150
    0
      src/main/java/org/terasology/kallisti/jnlua/KallistiGlobalRegistry.java
  43. 357
    0
      src/main/java/org/terasology/kallisti/oc/MachineOpenComputers.java
  44. 63
    0
      src/main/java/org/terasology/kallisti/oc/OCFont.java
  45. 247
    0
      src/main/java/org/terasology/kallisti/oc/OCGPUCommand.java
  46. 282
    0
      src/main/java/org/terasology/kallisti/oc/OCGPURenderer.java
  47. 291
    0
      src/main/java/org/terasology/kallisti/oc/OCPersistenceAPI.java
  48. 66
    0
      src/main/java/org/terasology/kallisti/oc/OCTextRenderer.java
  49. 5
    0
      src/main/java/org/terasology/kallisti/oc/OpenComputersBeeper.java
  50. 65
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCComputer.java
  51. 145
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCEEPROM.java
  52. 219
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCFilesystem.java
  53. 371
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCGPU.java
  54. 67
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCKeyboard.java
  55. 113
    0
      src/main/java/org/terasology/kallisti/oc/PeripheralOCScreen.java
  56. 74
    0
      src/main/java/org/terasology/kallisti/oc/ShimComponent.java
  57. 116
    0
      src/main/java/org/terasology/kallisti/oc/ShimComputer.java
  58. 135
    0
      src/main/java/org/terasology/kallisti/oc/ShimInvoker.java
  59. 48
    0
      src/main/java/org/terasology/kallisti/oc/ShimOS.java
  60. 42
    0
      src/main/java/org/terasology/kallisti/oc/ShimSystem.java
  61. 123
    0
      src/main/java/org/terasology/kallisti/oc/ShimUnicode.java
  62. 96
    0
      src/main/java/org/terasology/kallisti/oc/ShimUserdata.java
  63. 38
    0
      src/main/java/org/terasology/kallisti/oc/proxy/OCUserdataProxy.java
  64. 45
    0
      src/main/java/org/terasology/kallisti/oc/proxy/OCUserdataProxyList.java
  65. 31
    0
      src/main/java/org/terasology/kallisti/oc/proxy/OCUserdataProxyMap.java
  66. 129
    0
      src/main/java/org/terasology/kallisti/simulator/FSFileJavaIO.java
  67. 72
    0
      src/main/java/org/terasology/kallisti/simulator/FSMetadataJavaIO.java
  68. 68
    0
      src/main/java/org/terasology/kallisti/simulator/InMemoryStaticByteStorage.java
  69. 99
    0
      src/main/java/org/terasology/kallisti/simulator/Main.java
  70. 115
    0
      src/main/java/org/terasology/kallisti/simulator/Simulator.java
  71. 68
    0
      src/main/java/org/terasology/kallisti/simulator/SimulatorComponentContext.java
  72. 107
    0
      src/main/java/org/terasology/kallisti/simulator/SimulatorFileSystem.java
  73. 118
    0
      src/main/java/org/terasology/kallisti/simulator/SimulatorFrameBufferWindow.java
  74. 58
    0
      src/main/java/org/terasology/kallisti/simulator/SimulatorInstantiationManager.java
  75. 68
    0
      src/main/java/org/terasology/kallisti/simulator/SimulatorKeyboardInputWindow.java
  76. 103
    0
      src/main/java/pl/asie/papercomputers/MapRendererListener.java
  77. 130
    0
      src/main/java/pl/asie/papercomputers/PaperComputersPlugin.java
  78. 46
    0
      src/main/java/pl/asie/papercomputers/commands/ComputerItemCommand.java
  79. 10
    0
      src/main/java/pl/asie/papercomputers/computer/BlockComponentContext.java
  80. 58
    0
      src/main/java/pl/asie/papercomputers/computer/BukkitComponentContext.java
  81. 54
    0
      src/main/java/pl/asie/papercomputers/computer/ComponentEntry.java
  82. 277
    0
      src/main/java/pl/asie/papercomputers/computer/ComponentTracker.java
  83. 138
    0
      src/main/java/pl/asie/papercomputers/computer/ComputerState.java
  84. 9
    0
      src/main/java/pl/asie/papercomputers/computer/InventoryComponentContext.java
  85. 147
    0
      src/main/java/pl/asie/papercomputers/computer/ScreenComponent.java
  86. 49
    0
      src/main/java/pl/asie/papercomputers/computer/ScreenComponentEntry.java
  87. 73
    0
      src/main/java/pl/asie/papercomputers/input/InputProvider.java
  88. 49
    0
      src/main/java/pl/asie/papercomputers/input/PaperKeyboardProvider.java
  89. 68
    0
      src/main/java/pl/asie/papercomputers/items/CustomItem.java
  90. 17
    0
      src/main/java/pl/asie/papercomputers/items/CustomItemListener.java
  91. 23
    0
      src/main/java/pl/asie/papercomputers/items/CustomItemTypes.java
  92. 34
    0
      src/main/java/pl/asie/papercomputers/items/FilesystemItem.java
  93. 46
    0
      src/main/java/pl/asie/papercomputers/screen/ItemFrameEntry.java
  94. 26
    0
      src/main/java/pl/asie/papercomputers/screen/ItemFrameEntryAddedEvent.java
  95. 26
    0
      src/main/java/pl/asie/papercomputers/screen/ItemFrameEntryRemovedEvent.java
  96. 199
    0
      src/main/java/pl/asie/papercomputers/screen/ItemFrameTracker.java
  97. 65
    0
      src/main/java/pl/asie/papercomputers/screen/Screen.java
  98. 143
    0
      src/main/java/pl/asie/papercomputers/screen/ScreenTracker.java
  99. 9
    0
      src/main/resources/config.yml
  100. 0
    0
      src/main/resources/plugin.yml

+ 54
- 0
build.gradle View File

@@ -0,0 +1,54 @@
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'com.github.johnrengelman.shadow'

buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.4'
}
}

configurations {
shadow
}

sourceCompatibility = 1.8

def ENV = System.getenv()
if (ENV.BUILD_NUMBER) {
version = version + "." + "${System.getenv().BUILD_NUMBER}"
}

group = 'pl.asie.papercomputers'
archivesBaseName = project.name.toLowerCase()

repositories {
mavenCentral()
maven {
url "http://artifactory.terasology.org/artifactory/virtual-repo-live"
}
maven {
name = 'DestroysTokyo'
url = 'https://papermc.io/repo/repository/maven-public/'
}
}

dependencies {
compile 'com.destroystokyo.paper:paper-api:1.13.2-R0.1-SNAPSHOT'
compile group: 'org.terasology.jnlua', name: 'JNLua', version: '0.1.0-SNAPSHOT'
shadow group: 'org.terasology.jnlua', name: 'JNLua', version: '0.1.0-SNAPSHOT'
}

task allJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
configurations = [project.configurations.shadow]
classifier = 'all'
from(project.sourceSets.main.output)
}

build.dependsOn allJar

+ 3
- 0
gradle.properties View File

@@ -0,0 +1,3 @@
name = PaperComputers
description = Computers made out of Paper
version = 0.1.0

BIN
gradle/wrapper/gradle-wrapper.jar View File


+ 6
- 0
gradle/wrapper/gradle-wrapper.properties View File

@@ -0,0 +1,6 @@
#Tue Aug 16 01:28:54 BST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip

+ 164
- 0
gradlew View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# 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
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=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
echo "$*"
}

die ( ) {
echo
echo "$*"
echo
exit 1
}

# 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
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
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"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

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.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
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
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

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; 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
# 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\""
fi
i=$((i+1))
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

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90
- 0
gradlew.bat View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="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

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega

+ 31
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentContext.java View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import org.terasology.kallisti.base.interfaces.Identifiable;

/**
* A ComponentContext is an object used for identifying components from
* external sources inside a Kallisti virtual machine. It should be implemented
* in a way allowing, given a ComponentContext, to find the in-engine location
* of a Kallisti component's provider.
*
* Please note that a valid ComponentContext must implement equals() and
* hashCode().
*/
public interface ComponentContext extends Identifiable {
}

+ 24
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentEvent.java View File

@@ -0,0 +1,24 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

/**
* The base class for any event emitted to a given Machine's Components.
*/
public abstract class ComponentEvent {

}

+ 90
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentEventHandler.java View File

@@ -0,0 +1,90 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import org.terasology.kallisti.base.util.CollectionBackedMultiValueMap;
import org.terasology.kallisti.base.util.MultiValueMap;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

/**
* Utility class which stores listeners for each given ComponentEvent and
* allows emitting said events.
*
* @see ComponentEvent
*/
public class ComponentEventHandler {
private static class Listener {
private final Object parent;
private final MethodHandle handle;

public Listener(Object parent, Method method) {
this.parent = parent;
try {
this.handle = MethodHandles.lookup().unreflect(method);
} catch (IllegalAccessException e) {
// Should be caught earlier!
throw new RuntimeException(e);
}
}

public void invoke(ComponentEvent event) throws Throwable {
this.handle.invoke(parent, event);
}
}

private final MultiValueMap<Class, Listener> listeners;

public ComponentEventHandler() {
listeners = new CollectionBackedMultiValueMap<>(new HashMap<>(), ArrayList::new);
}

/**
* Register an object's @ComponentEventListener-marked methods
* as event listeners.
* @param o The object.
*/
public void register(Object o) {
for (Method m : o.getClass().getMethods()) {
if (m.getAnnotation(ComponentEventListener.class) != null
&& m.getParameterCount() == 1
&& !m.isVarArgs()
&& ComponentEvent.class.isAssignableFrom(m.getParameterTypes()[0])) {
listeners.add(m.getParameterTypes()[0], new Listener(o, m));
}
}
}

/**
* Emit a given ComponentEvent to all matching listeners.
* @param event The event.
*/
public void emit(ComponentEvent event) {
for (Listener l : listeners.values(event.getClass())) {
try {
l.invoke(event);
} catch (Throwable t) {
t.printStackTrace();
// TODO: pass?
}
}
}
}

+ 37
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentEventListener.java View File

@@ -0,0 +1,37 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Interface used to mark ComponentEvent-listening methods in components. Those
* methods will be called by ComponentEventHandler when such an event is
* emitted.
*
* The signature is as follows:
* void eventListener([? extends ComponentEvent] event) { ... }
*
* @see ComponentEventHandler
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ComponentEventListener {
}

+ 38
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentInterface.java View File

@@ -0,0 +1,38 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks an interface (or class) used in type-based component queries. This
* imposes a limitation of only having one object of a given class (and all
* subclasses or implementing classes) per unique ComponentContext instance.
*
* Please note that this annotation is not necessary for a Component object,
* however retrieval of a component "by Class" from a Machine will NOT be
* possible without it.
*
* @see ComponentContext
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentInterface {
}

+ 51
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentMethod.java View File

@@ -0,0 +1,51 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

/**
* Marks a method in an object-based peripheral which should be exposed
* to the end user.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ComponentMethod {
/**
* @return The target name of the method. If empty, the Java method name
* is used.
*/
String name() default "";

/**
* @return If this is true and the method's return type is an array,
* the array will be returned as multiple arguments on compatible
* platforms.
*/
boolean returnsMultipleArguments() default false;

/**
* @return If the method execution requires synchronization with the
* in-game execution thread, as opposed to running on the
* computer's separate thread.
*/
boolean synchronize() default false;
}

+ 46
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentRule.java View File

@@ -0,0 +1,46 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks a method or constructor which, taking in multiple arguments, creates
* a new component.
*
* Arguments may be components or provided non-component objects, such
* as a Machine. All permutations of them will be evaluated with the Rule.
* All objects are required to be present at least once, unless Optional is
* utilized. The Contexts of all Components will be joined together.
*
* There are two types of rules:
* - creation rules - non-variable argument methods,
* - joining rules - variable argument methods returning the same type as
* their argument.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface ComponentRule {
/**
* The priority of the rule. Higher priorities are attempted first.
* @return The priority of the rule.
*/
int priority() default 0;
}

+ 35
- 0
src/main/java/org/terasology/kallisti/base/component/ComponentTickEvent.java View File

@@ -0,0 +1,35 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

/**
* This ComponentEvent is emitted once every machine tick.
*/
public class ComponentTickEvent extends ComponentEvent {
private final double tickTime;

public ComponentTickEvent(double tickTime) {
this.tickTime = tickTime;
}

/**
* @return The duration of the tick in question, in seconds.
*/
public double getTickTime() {
return tickTime;
}
}

+ 623
- 0
src/main/java/org/terasology/kallisti/base/component/Machine.java View File

@@ -0,0 +1,623 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

import org.terasology.kallisti.base.interfaces.Persistable;
import org.terasology.kallisti.base.util.KallistiReflect;
import org.terasology.kallisti.base.util.CollectionBackedMultiValueMap;
import org.terasology.kallisti.base.util.MultiValueMap;

import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
* The base class for a Machine, implementing component management logic.
*/
public abstract class Machine {
public enum MachineState {
UNINITIAILIZED,
RUNNING,
STOPPED
}

private interface Rule {
int getPriority();
Class[] getInput();
Type[] getInputGeneric();
Class getOutput();
Object invoke(Object... input) throws Throwable;
}

private static class RuleMethod implements Rule {
private final Object parent;
private final Method method;
private final int priority;

RuleMethod(Object parent, Method method, int priority) {
this.parent = parent;
this.method = method;
this.priority = priority;
}

@Override
public Class getOutput() {
return method.getReturnType();
}

@Override
public Class[] getInput() {
return method.getParameterTypes();
}

@Override
public Type[] getInputGeneric() {
return method.getGenericParameterTypes();
}

@Override
public Object invoke(Object... input) throws Throwable {
return method.invoke(parent, input);
}

@Override
public int getPriority() {
return priority;
}
}

private static class RuleConstructor implements Rule {
private final Constructor method;
private final int priority;

RuleConstructor(Constructor method, int priority) {
this.method = method;
this.priority = priority;
}

@Override
public Class getOutput() {
return method.getDeclaringClass();
}

@Override
public Class[] getInput() {
return method.getParameterTypes();
}

@Override
public Type[] getInputGeneric() {
return method.getGenericParameterTypes();
}

@Override
public Object invoke(Object... input) throws Throwable {
return method.newInstance(input);
}

@Override
public int getPriority() {
return priority;
}
}

private static class ComponentEntry {
private final ComponentContext context;
private final Object object;

public ComponentEntry(ComponentContext context, Object object) {
this.context = context;
this.object = object;
}

@Override
public int hashCode() {
return 31 * context.hashCode() + object.hashCode();
}

@Override
public boolean equals(Object o) {
if (!(o instanceof ComponentEntry)) {
return false;
} else {
return ((ComponentEntry) o).context.equals(context)
&& ((ComponentEntry) o).object.equals(object);
}
}
}

protected final ComponentEventHandler eventHandler;

// Initialized by addComponent();
private final List<ComponentEntry> entries;
private final Map<Object, ComponentEntry> entriesByObject;
private final Map<Class, Map<ComponentContext, ComponentEntry>> entryClassContextTable;

// Used by .initialize()
private final MultiValueMap<Class, Object> nonEntryObjectsByClass;

// Initialized by registerRules()
private final List<Rule> creationRules;
private final MultiValueMap<Class, Rule> linkingRules;
private boolean rulesListDirty;

// Initialized in .initialize();
protected MachineState state;

public Machine() {
eventHandler = new ComponentEventHandler();

creationRules = new ArrayList<>();
linkingRules = new CollectionBackedMultiValueMap<>(new IdentityHashMap<>(), ArrayList::new);

entries = new ArrayList<>();
entriesByObject = new IdentityHashMap<>();
entryClassContextTable = new HashMap<>();
nonEntryObjectsByClass = new CollectionBackedMultiValueMap<>(new IdentityHashMap<>(), ArrayList::new);

state = MachineState.UNINITIAILIZED;

addNonComponentObject(this);
}

/**
* @return All component objects inside a given Machine.
*/
public Iterable<? extends Object> getAllComponents() {
return entries.stream().map((m) -> m.object).collect(Collectors.toList());
}

/**
* @return All component contexts inside a given Machine.
*/
public Iterable<ComponentContext> getAllComponentContexts() {
return entries.stream().map((m) -> m.context).collect(Collectors.toList());
}

private void registerRules(Object parent, Method m) throws IllegalArgumentException {
ComponentRule rule = m.getAnnotation(ComponentRule.class);
if (rule != null) {
RuleMethod r = new RuleMethod(parent, m, rule.priority());
if (r.method.getParameterCount() == 0) {
throw new IllegalArgumentException("Rule must accept a parameter! If you want to always add a component to a machine, do so directly.");
}

if (r.method.isVarArgs()) {
if (r.method.getParameterCount() > 1) {
throw new IllegalArgumentException("Linking rule may only have one variable argument!");
} else {
Class c = r.getInput()[0];
linkingRules.add(c, r);
}
} else {
creationRules.add(r);
}
}

rulesListDirty = true;
}

/**
* Register the component rules present in static methods and constructors
* of the given class.
*
* @see ComponentRule
*
* @param c The given class.
* @throws IllegalArgumentException
*/
public void registerRules(Class c) throws IllegalArgumentException {
for (Method m : c.getMethods()) {
if ((m.getModifiers() & Modifier.STATIC) != 0) {
registerRules(null, m);
}
}

for (Constructor cc : c.getConstructors()) {
ComponentRule ruleC = (ComponentRule) cc.getAnnotation(ComponentRule.class);
if (ruleC != null) {
if (cc.getParameterCount() == 0) {
throw new IllegalArgumentException("Rule must accept a parameter! If you want to always add a component to a machine, do so directly.");
}

RuleConstructor rule = new RuleConstructor(cc, ruleC.priority());

if (cc.isVarArgs()) {
if (cc.getParameterCount() > 1) {
throw new IllegalArgumentException("Linking rule may only have one variable argument!");
} else {
linkingRules.add(cc.getDeclaringClass(), rule);
}
} else {
creationRules.add(rule);
}
}
}
}

/**
* Register the component rules present in methods of the given object.
*
* @see ComponentRule
*
* @param o The given object.
* @throws IllegalArgumentException
*/
public void registerRules(Object o) throws IllegalArgumentException {
for (Method m : o.getClass().getMethods()) {
registerRules(o, m);
}
}

protected <T> T join(T... o) {
if (o.length == 0) {
return null;
} else if (o.length == 1) {
return o[0];
} else {
boolean match = true;
for (int i = 1; i < o.length; i++) {
if (o[0] != o[i]) {
match = false;
break;
}
}

if (match) {
return o[0];
}

for (Rule r : linkingRules.values(o.getClass())) {
try {
Object result = r.invoke(o);
if (result != null) {
return (T) result;
}
} catch (Throwable e) {
// pass?
}
}

throw new RuntimeException("Does not understand how to join " + o[0].getClass() + " objects!");
}
}

/**
* Add a non-component object accessible to components during creation
* via ComponentRules.
*
* In general, these should be objects related to a given Machine, such
* as the Machine itself.
*
* @see ComponentRule
*
* @param o The non-component object.
* @return Whether the addition was successful.
*/
protected boolean addNonComponentObject(Object o) {
for (Class c : KallistiReflect.classes(o.getClass())) {
if (!nonEntryObjectsByClass.contains(c, o)) {
nonEntryObjectsByClass.add(c, o);
}
}

return true;
}

private static final Map<Class, Boolean> isComponentItfMap = new HashMap<>();

/**
* @param c Given class.
* @return True if the given class is a subclass of a class annotated
* with @ComponentInterface and/or implements one, false otherwise.
*/
private static boolean isComponentInterface(Class c) {
return isComponentItfMap.computeIfAbsent(c,
(cc) -> {
for (Class cl : KallistiReflect.classes(cc)) {
if (cl.getAnnotation(ComponentInterface.class) != null) {
return true;
}
}

return false;
});
}

/**
* Add a new component to the Machine. If a @ComponentInterface-marked
* class or subclass is already present with the same context, the
* component will not be added.
*
* @see ComponentInterface
*
* @param context The component's context.
* @param o The component object.
* @return Whether or not the addition was successful.
*/
public boolean addComponent(ComponentContext context, Object o) {
ComponentEntry entry = new ComponentEntry(context, o);
Class objectClass = entry.object.getClass();

for (Class c : KallistiReflect.classes(objectClass)) {
if (isComponentInterface(c)) {
Map<ComponentContext, ComponentEntry> m = entryClassContextTable.computeIfAbsent(c, (cc -> new HashMap<>()));
if (m.containsKey(context)) {
return false;
}
}
}

entries.add(entry);
entriesByObject.put(entry.object, entry);

for (Class c : KallistiReflect.classes(objectClass)) {
if (isComponentInterface(c)) {
Map<ComponentContext, ComponentEntry> m = entryClassContextTable.computeIfAbsent(c, (cc -> new HashMap<>()));
m.put(context, entry);
}
}

// Apply rules
if (rulesListDirty) {
// Sort rules
Comparator<Rule> sorter = (rule1, rule2) -> Integer.compare(rule2.getPriority(), rule1.getPriority());
creationRules.sort(sorter);
for (Class c : linkingRules.keys()) {
((List<Rule>) linkingRules.values(c)).sort(sorter);
}

rulesListDirty = false;
}

boolean addedNewComponents = true;
while (addedNewComponents) {
addedNewComponents = false;

for (Rule r : creationRules) {
List<List<Object>> baseComponents = new ArrayList<>();
int permutations = 1;

Class[] inp = r.getInput();
Type[] inpGeneric = r.getInputGeneric();

for (int i = 0; i < inp.length; i++) {
Class c = inp[i];
boolean required = true;
if (c == Optional.class && inpGeneric[i] instanceof ParameterizedType) {
required = false;
c = (Class) (((ParameterizedType) inpGeneric[i]).getActualTypeArguments()[0]);
}

Collection ccol = entryClassContextTable.getOrDefault(c, Collections.emptyMap()).values();
if (ccol.isEmpty()) {
ccol = nonEntryObjectsByClass.values(c);
}

if (required || !ccol.isEmpty()) {
permutations *= ccol.size();
if (permutations == 0) {
break;
}
}

baseComponents.add(ccol instanceof List ? (List) ccol : new ArrayList<>(ccol));
}

for (int i = 0; i < permutations; i++) {
List<ComponentContext> contexts = new ArrayList<>();
Object[] params = new Object[baseComponents.size()];
int iCurr = i;
for (int j = 0; j < params.length; j++) {
if (!baseComponents.get(j).isEmpty()) {
Object oFound = baseComponents.get(j).get(iCurr % baseComponents.get(j).size());
if (oFound instanceof ComponentEntry) {
ComponentEntry entryFound = (ComponentEntry) oFound;
contexts.add(entryFound.context);
params[j] = entryFound.object;
} else {
// non-entry object
params[j] = oFound;
}

if (inp[j] == Optional.class) {
params[j] = Optional.ofNullable(params[j]);
}

iCurr /= baseComponents.get(j).size();
}
}

try {
Object result = r.invoke(params);
if (result != null) {
ComponentContext contextJoined = join(contexts.toArray(new ComponentContext[0]));
if (getComponent(contextJoined, result.getClass()) == null) {
addedNewComponents |= addComponent(contextJoined, result);
}
}
} catch (Throwable e) {
// TODO: forward
throw new RuntimeException(e);
}
}
}
}

eventHandler.register(o);

return true;
}

/**
* Removes a component from the Machine.
*
* @param context The component's context.
* @return Whether or not the component has been removed.
*/
public boolean removeComponent(ComponentContext context) {
// TODO: Remove joined contexts?
List<ComponentEntry> entriesToRemove = entries.stream()
.filter((a) -> a.context.equals(context))
.collect(Collectors.toList());

if (entriesToRemove.isEmpty()) {
return false;
}

for (ComponentEntry e : entriesToRemove) {
entries.remove(e);
entriesByObject.remove(e.object);

for (Class c : KallistiReflect.classes(e.object.getClass())) {
Map<ComponentContext, ComponentEntry> m = entryClassContextTable.get(c);
if (m != null) {
m.remove(context);
}
}
}

return true;
}

/**
* Initialize the machine.
*/
public void initialize() {
if (state != MachineState.UNINITIAILIZED) {
throw new RuntimeException("Already initialized!");
}

state = MachineState.STOPPED;
}

/**
* Retrieve a component. May return null.
*
* @param context The context of the component.
* @param c The class of the component. Should be a (sub)class or
* (sub)interface marked with @ComponentInterface.
* @param <T> The type of the component.
* @return The component, or null if not present.
*/
@SuppressWarnings("unchecked")
public <T> T getComponent(ComponentContext context, Class<T> c) {
ComponentEntry entry = entryClassContextTable
.getOrDefault(c, Collections.emptyMap())
.get(context);

return entry != null ? (T) entry.object : null;
}

/**
* Retrieve all component objects of a given class type
* (or a subclass/implementation).
*
* @param c The class of the component.
* @param <T> The type of the component.
* @return The component, or null if not present.
*/
@SuppressWarnings("unchecked")
public <T> Collection<T> getComponentsByClass(Class<T> c) {
return entryClassContextTable
.getOrDefault(c, Collections.emptyMap())
.values().stream().map((e) -> (T) e.object).collect(Collectors.toList());
}

/**
* Retrieve all component contexts of a given class type
* (or a subclass/implementation).
*
* @param c The class of the component.
* @return The component context, or null if not present.
*/
public Collection<ComponentContext> getContextsByClass(Class c) {
return entryClassContextTable
.getOrDefault(c, Collections.emptyMap())
.values().stream().map((e) -> e.context).collect(Collectors.toList());
}

/**
* Get the context of a given component object.
*
* @param component The component object.
* @return The context of it.
*/
public ComponentContext getContext(Object component) {
return entriesByObject.get(component).context;
}

/**
* @return The current machine state.
*/
public final MachineState getState() {
return state;
}

/**
* Start the machine simulation. If the machine is already running,
* throws MachineInvalidStateException.
*
* @throws Exception If something goes wrong during startup.
*/
public final void start() throws Exception {
if (state != MachineState.STOPPED) {
throw new MachineInvalidStateException(state);
}

startInternal();
state = MachineState.RUNNING;
}
protected abstract void startInternal() throws Exception;

/**
* Stop the machine simulation. If the machine is not running,
* throws MachineInvalidStateException.
*
* @throws Exception If something goes wrong during shutdown.
*/
public final void stop() throws Exception {
if (state != MachineState.RUNNING) {
throw new MachineInvalidStateException(state);
}

stopInternal();
state = MachineState.STOPPED;
}

protected abstract void stopInternal() throws Exception;

/**
* Run one tick of the computer, lasting a given amount of time.
* @throws Exception
*/
public final boolean tick(double time) throws Exception {
eventHandler.emit(new ComponentTickEvent(time));
return tickInternal(time);
}

protected abstract boolean tickInternal(double time) throws Exception;

/**
* Get the persistence handler, if the machine can be persisted or unpersisted.
*
* @return The persistence handler if present.
*/
public Optional<Persistable> getPersistenceHandler() {
return Optional.empty();
}
}

+ 29
- 0
src/main/java/org/terasology/kallisti/base/component/MachineInvalidStateException.java View File

@@ -0,0 +1,29 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

/**
* Exception thrown if a machine's current state is invalid for a given
* operation (starting, stopping, etc.)
*
* @see Machine.MachineState
*/
public class MachineInvalidStateException extends Exception {
public MachineInvalidStateException(Machine.MachineState current) {
super("Invalid state " + current);
}
}

+ 29
- 0
src/main/java/org/terasology/kallisti/base/component/Peripheral.java View File

@@ -0,0 +1,29 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.component;

/**
* Interface for Components which serve as Peripherals for a given computer,
* allowing querying by type and ensuring only one of each class is available
* per context.
*
* @see ComponentInterface
*/
@ComponentInterface
public interface Peripheral {
String type();
}

+ 31
- 0
src/main/java/org/terasology/kallisti/base/interfaces/ConnectedContext.java View File

@@ -0,0 +1,31 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentContext;

import java.util.List;

/**
* Interface for Contexts which are connected with other Components in
* a neighbor-type fashion.
*
* TODO: Is this temporary?
*/
public interface ConnectedContext {
List<ComponentContext> getNeighbors();
}

+ 214
- 0
src/main/java/org/terasology/kallisti/base/interfaces/FileSystem.java View File

@@ -0,0 +1,214 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentInterface;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
* A component interface for exposing file-based filesystems to computers.
*/
@ComponentInterface
public interface FileSystem {
interface Metadata {
/**
* @return The name of the file.
*/
String name();

/**
* @return The full path of the file.
*/
String path();

/**
* @return Whether or not the file is a directory.
*/
boolean isDirectory();

/**
* @return Whether or not the file can be read.
*/
boolean canRead();

/**
* @return Whether or not the file can be written to.
*/
boolean canWrite();

/**
* @return The file's creation time.
*/
Date creationTime();

/**
* @return The file's time of last modification.
*/
Date modificationTime();

/**
* @return The size of the file, in bytes.
*/
long size();
}

enum OpenMode {
READ,
WRITE,
APPEND
}

enum Whence {
/**
* Offsets a seek operation to the beginning of a file.
*/
BEGINNING,
/**
* Offsets a seek operation to the current position in a file.
*/
CURRENT,
/**
* Offsets a seek operation to the end of a file.
*/
END
}

interface File extends AutoCloseable {
/**
* @return True if seek() is a valid operation on this file.
*/
boolean isSeekable();
/**
* @return True if read() is a valid operation on this file.
*/
boolean isReadable();
/**
* @return True if write() is a valid operation on this file.
*/
boolean isWritable();

/**
* Seek to a given position in the file.
* @param whence The starting location of a seek.
* @param offset The offset to add to the location, in bytes.
* @return True if the seek operation was successful, false otherwise.
* @throws IOException If something goes wrong with the seek.
*/
long seek(Whence whence, int offset) throws IOException;

/**
* Read a given amount of bytes in a file, at current position, and
* advance the file pointer.
* @param bytes The amount of bytes to read.
* @return A byte array of at least length 1 if bytes were read, null otherwise.
* @throws IOException If something goes wrong with the read.
*/
byte[] read(int bytes) throws IOException;

/**
* Write a given byte array to a file, at current position, and
* advance the file pointer.
* @param value The byte array to write.
* @param offset The offset in the byte array, in bytes.
* @param len The amount of data from the byte array to write, in bytes.
* @return True if the write was successful, false otherwise.
* @throws IOException If something goes wrong with the write.
*/
boolean write(byte[] value, int offset, int len) throws IOException;

default boolean write(byte[] value) throws IOException {
return write(value, 0, value.length);
}

default long seek(Whence whence) throws IOException {
return seek(whence, 0);
}

/**
* @return The current position in the file, or -1 if the file is not
* seekable.
* @throws IOException If something goes wrong with the seek.
*/
default long position() throws IOException {
return isSeekable() ? seek(Whence.CURRENT, 0) : -1;
}
}

/**
* List the contents of a directory.
* @param path The directory.
* @return A collections of all contents in the directory.
* @throws IOException
*/
Collection<Metadata> list(String path) throws IOException;

/**
* Open a file.
* @param path The path to the file.
* @param mode The opening mode.
* @return The opened File.
* @throws IOException
*/
File open(String path, OpenMode mode) throws IOException;

/**
* Retrieve metadata pertaining to a file or directory.
* @param path The path to the file or directory.
* @return The metadata of the file or directory.
* @throws FileNotFoundException
*/
Metadata metadata(String path) throws FileNotFoundException;

/**
* Attempt creating a directory.
* @param path The new directory path.
* @return Whether or not the deletion was successful.
* @throws IOException
*/
boolean createDirectory(String path) throws IOException;

/**
* Attempt deleting a file.
* @param path The file path.
* @return Whether or not the deletion was successful.
* @throws IOException
*/
boolean delete(String path) throws IOException;

/**
* @return The total size of this filesystem, in bytes.
*/
long getTotalAreaBytes();

/**
* @return The amount of bytes used by data on this filesystem.
*/
long getUsedAreaBytes();

/**
* @return The amount of bytes free for data on this filesystem.
*/
default long getFreeAreaBytes() {
return Math.max(0, getTotalAreaBytes() - getUsedAreaBytes());
}
}

+ 44
- 0
src/main/java/org/terasology/kallisti/base/interfaces/FrameBuffer.java View File

@@ -0,0 +1,44 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentInterface;
import org.terasology.kallisti.base.util.Dimension;
import org.terasology.kallisti.base.util.PixelDimension;

import java.io.IOException;
import java.io.InputStream;

/**
* TODO: Is this temporary?
*/
@ComponentInterface
public interface FrameBuffer {
interface Image {
PixelDimension size();
int[] data();
}

interface Renderer extends Synchronizable.Receiver {
void update(InputStream stream) throws IOException;
void render(FrameBuffer buffer);
}

void bind(Synchronizable source, Renderer renderer);
Dimension aspectRatio();
void blit(Image image);
}

+ 27
- 0
src/main/java/org/terasology/kallisti/base/interfaces/Identifiable.java View File

@@ -0,0 +1,27 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

/**
* Interface for contexts which have a unique identifier.
*/
public interface Identifiable {
/**
* @return The unique identifier of the context.
*/
String identifier();
}

+ 75
- 0
src/main/java/org/terasology/kallisti/base/interfaces/KeyboardInputProvider.java View File

@@ -0,0 +1,75 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentInterface;

/**
* Engine-side interface for providing keyboard input to the computer.
*/
@ComponentInterface
public interface KeyboardInputProvider {
enum KeyType {
PRESSED,
RELEASED
}

class Key {
/**
* The type of the registered key operation.
*/
private final KeyType type;

/**
* The key code, as defined by Java's VK_ constants.
*/
private final int code;

/**
* The codepoint of the key, or -1 if none.
*/
private final int chr;

public Key(KeyType type, int code, int chr) {
this.type = type;
this.code = code;
this.chr = chr;
}

public KeyType getType() {
return type;
}

public int getCode() {
return code;
}

public int getChar() {
return chr;
}
}

/**
* @return Whether or not there's a key waiting.
*/
boolean hasNextKey();

/**
* @return The waiting key, which is then removed.
*/
Key nextKey();
}

+ 26
- 0
src/main/java/org/terasology/kallisti/base/interfaces/Labelable.java View File

@@ -0,0 +1,26 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

/**
* A generic interface for objects which can be labeled. Primarily used on
* ComponentContexts.
*/
public interface Labelable {
String getLabel();
boolean setLabel(String label);
}

+ 33
- 0
src/main/java/org/terasology/kallisti/base/interfaces/Persistable.java View File

@@ -0,0 +1,33 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentInterface;
import org.terasology.kallisti.base.util.PersistenceException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* An interface for components whose data can be stored, persisted and read,
* including across version upgrades.
*/
public interface Persistable {
void persist(OutputStream data) throws IOException, PersistenceException;
void unpersist(InputStream data) throws IOException, PersistenceException;
}

+ 49
- 0
src/main/java/org/terasology/kallisti/base/interfaces/StaticByteStorage.java View File

@@ -0,0 +1,49 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import org.terasology.kallisti.base.component.ComponentInterface;

/**
* An interface for static-sized, byte array-backed storage.
*
* For example, OpenComputers EEPROMs, or a ROM used by an 8-bit
* microcomputer.
*/
@ComponentInterface
public interface StaticByteStorage {
/**
* Returns the backing byte array, which can also be queried for size.
*
* Please do not modify the byte array without first calling .canModify().
*
* @return The backing byte array.
*/
byte[] get();

/**
* @return Whether the byte array can be modified.
*/
default boolean canModify() {
return true;
}

/**
* Marks the backing byte array as having been modified.
*/
void markModified();
}

+ 65
- 0
src/main/java/org/terasology/kallisti/base/interfaces/Synchronizable.java View File

@@ -0,0 +1,65 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.interfaces;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* An interface for components which can be synchronized across the network,
* from the server to the client.
*/
public interface Synchronizable {
/**
* An interface for classes capable of receiving data from a matching
* Synchronizable.
*
* Generally, Receivers should store the binding to sources, as well
* as maintain synchronization of source data to the Receiver.
*/
interface Receiver {
void update(InputStream stream) throws IOException;
}

enum Type {
/**
* Intial update for a given client.
*/
INITIAL,
/**
* Any future update for a given client.
*/
DELTA
}

/**
* Check if the component has a ready synchronization packet of a given
* type.
* @param type The type.
* @return Whether a synchronization packet can be made or not.
*/
boolean hasSyncPacket(Type type);

/**
* Write the synchronization packet to the given output stream.
* @param type The packet type.
* @param stream The given output stream.
* @throws IOException Upon packet writing issues.
*/
void writeSyncPacket(Type type, OutputStream stream) throws IOException;
}

+ 29
- 0
src/main/java/org/terasology/kallisti/base/proxy/Proxy.java View File

@@ -0,0 +1,29 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.proxy;

public class Proxy<T> {
protected final T parent;

public Proxy(T parent) {
this.parent = parent;
}

public T getParent() {
return parent;
}
}

+ 22
- 0
src/main/java/org/terasology/kallisti/base/proxy/ProxyFactory.java View File

@@ -0,0 +1,22 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.proxy;

@FunctionalInterface
public interface ProxyFactory<T> {
Proxy<T> create(T object);
}

+ 103
- 0
src/main/java/org/terasology/kallisti/base/util/CollectionBackedMultiValueMap.java View File

@@ -0,0 +1,103 @@
/*
* Copyright 2018 Adrian Siekierka, MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.kallisti.base.util;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
* A basic implementation of a multi-value-per-key map, using a map of lists.
* @param <K> The key type.
* @param <V> The value type.
*/
public class CollectionBackedMultiValueMap<K, V> implements MultiValueMap<K, V> {
private final Map<K, Collection<V>> map;
private final Supplier<Collection<V>> listSupplier;

public CollectionBackedMultiValueMap(Map<K, Collection<V>> map, Supplier<Collection<V>> listSupplier) {
this.map = map;
this.listSupplier = listSupplier;
}