From 384b2047ac2c5d91bd7f3523e7936f1c1bf9fdba Mon Sep 17 00:00:00 2001 From: serega404 Date: Mon, 2 Nov 2020 16:30:14 +0300 Subject: [PATCH 01/24] Init crash reports --- .../client/ProgressiaClientMain.java | 12 ++ .../common/util/crash/Analyzer.java | 5 + .../common/util/crash/ContextProvider.java | 8 ++ .../util/crash/CrashReportGenerator.java | 134 ++++++++++++++++++ .../crash/analyzers/OutOfMemoryAnalyzer.java | 12 ++ .../crash/providers/OSContextProvider.java | 18 +++ src/main/resources/log4j2.xml | 4 - 7 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java diff --git a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java index c9e3f2c..369602e 100644 --- a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java +++ b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java @@ -20,6 +20,9 @@ package ru.windcorp.progressia.client; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import ru.windcorp.progressia.ProgressiaLauncher; +import ru.windcorp.progressia.common.util.crash.CrashReportGenerator; +import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer; +import ru.windcorp.progressia.common.util.crash.providers.OSContextProvider; public class ProgressiaClientMain { @@ -28,6 +31,15 @@ public class ProgressiaClientMain { public static void main(String[] args) { logger.info("App started!"); + CrashReportGenerator.registerProvider(new OSContextProvider()); + CrashReportGenerator.registerAnalyzer(new OutOfMemoryAnalyzer()); + try { + long[] ssdss = new long[1 << 30]; + } catch (Throwable t) + { + CrashReportGenerator.makeCrashReport(t, ""); + } + ProgressiaLauncher.launch(args, new ClientProxy()); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java new file mode 100644 index 0000000..47b9e76 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java @@ -0,0 +1,5 @@ +package ru.windcorp.progressia.common.util.crash; + +public interface Analyzer { + String getPrompt(Throwable throwable, String messageFormat, Object... args); +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java new file mode 100644 index 0000000..ad0d5d3 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java @@ -0,0 +1,8 @@ +package ru.windcorp.progressia.common.util.crash; + +import java.util.Map; + +public interface ContextProvider { + + Map provideContext(); +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java new file mode 100644 index 0000000..4c808dd --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -0,0 +1,134 @@ +package ru.windcorp.progressia.common.util.crash; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +public class CrashReportGenerator { + + private CrashReportGenerator() { + } + + final static File latestLogFile = new File("crash-reports/latest.log"); + + private static Collection providers = new ArrayList(); + private static Collection> providerResponse = new ArrayList>(); + + private static Collection analyzers = new ArrayList(); + private static Collection analyzerResponse = new ArrayList(); + + private static final Logger logger = LogManager.getLogger("crash"); + + static public void makeCrashReport(Throwable throwable, String messageFormat, Object... args) { + + StringBuilder output = new StringBuilder(); + + for (ContextProvider currentProvider : providers) { + if (currentProvider != null) { + providerResponse.add(currentProvider.provideContext()); + } + } + + if (throwable != null) { + for (Analyzer currentAnalyzer : analyzers) { + if (currentAnalyzer != null) { + analyzerResponse.add(currentAnalyzer.getPrompt(throwable, messageFormat, args)); + } + } + } + + for (Map currentProviderResponse : providerResponse) { + if (currentProviderResponse != null && !currentProviderResponse.isEmpty()) { + addSeparator(output); + for (Map.Entry entry : currentProviderResponse.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + } + } + + for (String currentPrompt : analyzerResponse) { + if (currentPrompt != null && !currentPrompt.isEmpty()) { + addSeparator(output); + output.append(currentPrompt).append("\n"); + } + } + + // Formatting to a human-readable string + StringWriter sink = new StringWriter(); + if (throwable != null) { + try { + throwable.printStackTrace(new PrintWriter(sink)); + } catch (Exception e) { + // PLAK + } + } else { + sink.append("Null"); + } + + logger.info("\n" + output.toString()); + logger.fatal("Stacktrace: \n" + sink.toString()); + + addSeparator(output); + + output.append("Stacktrace: \n"); + output.append(sink.toString()).append("\n"); + + try { + System.err.println(output.toString()); + } catch (Exception e) { + // PLAK + } + + + createFileForCrashReport(output); + createFileForLatestCrashReport(output); + + + System.exit(0); + } + + public static void createFileForCrashReport(StringBuilder sb) { + Date date = new Date(); + + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); + + File logFile = new File("crash-reports/" + "crash-" + dateFormat.format(date) + ".log"); + + try (FileOutputStream fos = new FileOutputStream(logFile)) { + byte[] buffer = sb.toString().getBytes(); + + fos.write(buffer, 0, buffer.length); + } catch (IOException ex) { + // Crash Report not created + } + } + + public static void createFileForLatestCrashReport(StringBuilder sb) { + try (FileOutputStream fos = new FileOutputStream(latestLogFile)) { + byte[] buffer = sb.toString().getBytes(); + + fos.write(buffer, 0, buffer.length); + } catch (IOException ex) { + // Crash Report not created + } + } + + public static void registerProvider(ContextProvider provider) { + providers.add(provider); + } + + public static void registerAnalyzer(Analyzer analyzer) { + analyzers.add(analyzer); + } + + private static void addSeparator(StringBuilder sb) { + sb.append("-------------------------------------------------").append("\n"); + } +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java new file mode 100644 index 0000000..e750cea --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java @@ -0,0 +1,12 @@ +package ru.windcorp.progressia.common.util.crash.analyzers; + +import ru.windcorp.progressia.common.util.crash.Analyzer; + +public class OutOfMemoryAnalyzer implements Analyzer { + @Override + public String getPrompt(Throwable throwable, String messageFormat, Object... args) { + if (throwable instanceof OutOfMemoryError) + return "Try add memory for the JVM"; + return null; + } +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java new file mode 100644 index 0000000..712ee17 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java @@ -0,0 +1,18 @@ +package ru.windcorp.progressia.common.util.crash.providers; + +import ru.windcorp.progressia.common.util.crash.ContextProvider; + +import java.util.HashMap; +import java.util.Map; + +public class OSContextProvider implements ContextProvider { + + @Override + public Map provideContext() { + Map theThings = new HashMap<>(); + theThings.put("Name OS", System.getProperty("os.name")); + theThings.put("Version OS", System.getProperty("os.version")); + theThings.put("Architecture OS", System.getProperty("os.arch")); + return theThings; + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 29aaea2..c40552b 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -13,18 +13,14 @@ - - - - From e7e54d0ffdc664bed3067bf51a756e5d18c42ef4 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Mon, 2 Nov 2020 17:27:17 +0300 Subject: [PATCH 02/24] Code review --- .../client/ProgressiaClientMain.java | 3 +- .../common/util/crash/Analyzer.java | 2 +- .../common/util/crash/ContextProvider.java | 1 - .../util/crash/CrashReportGenerator.java | 55 +++++++++---------- .../crash/analyzers/OutOfMemoryAnalyzer.java | 2 +- .../crash/providers/OSContextProvider.java | 10 ++-- 6 files changed, 34 insertions(+), 39 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java index 369602e..84fa145 100644 --- a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java +++ b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java @@ -34,10 +34,11 @@ public class ProgressiaClientMain { CrashReportGenerator.registerProvider(new OSContextProvider()); CrashReportGenerator.registerAnalyzer(new OutOfMemoryAnalyzer()); try { + @SuppressWarnings("unused") long[] ssdss = new long[1 << 30]; } catch (Throwable t) { - CrashReportGenerator.makeCrashReport(t, ""); + CrashReportGenerator.makeCrashReport(t, "u %s stupid", "vry"); } ProgressiaLauncher.launch(args, new ClientProxy()); diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java index 47b9e76..0afdb1d 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java @@ -1,5 +1,5 @@ package ru.windcorp.progressia.common.util.crash; public interface Analyzer { - String getPrompt(Throwable throwable, String messageFormat, Object... args); + String analyze(Throwable throwable, String messageFormat, Object... args); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java index ad0d5d3..06150f0 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java @@ -3,6 +3,5 @@ package ru.windcorp.progressia.common.util.crash; import java.util.Map; public interface ContextProvider { - Map provideContext(); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java index 4c808dd..c51c125 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -13,50 +13,47 @@ import java.util.Map; public class CrashReportGenerator { - private CrashReportGenerator() { - } + private CrashReportGenerator() {} - final static File latestLogFile = new File("crash-reports/latest.log"); + private static final File LATEST_LOG_FILE = new File("crash-reports/latest.log"); - private static Collection providers = new ArrayList(); - private static Collection> providerResponse = new ArrayList>(); + private static final Collection PROVIDERS = new ArrayList<>(); + private static final Collection> PROVIDER_RESPONSES = new ArrayList<>(); - private static Collection analyzers = new ArrayList(); - private static Collection analyzerResponse = new ArrayList(); + private static final Collection ANALYZER = new ArrayList<>(); + private static final Collection ANALYZER_RESPONSES = new ArrayList<>(); - private static final Logger logger = LogManager.getLogger("crash"); + private static final Logger LOGGER = LogManager.getLogger("crash"); - static public void makeCrashReport(Throwable throwable, String messageFormat, Object... args) { + public static void makeCrashReport(Throwable throwable, String messageFormat, Object... args) { StringBuilder output = new StringBuilder(); - for (ContextProvider currentProvider : providers) { - if (currentProvider != null) { - providerResponse.add(currentProvider.provideContext()); + for (ContextProvider provider : PROVIDERS) { + if (provider != null) { + PROVIDER_RESPONSES.add(provider.provideContext()); } } - if (throwable != null) { - for (Analyzer currentAnalyzer : analyzers) { - if (currentAnalyzer != null) { - analyzerResponse.add(currentAnalyzer.getPrompt(throwable, messageFormat, args)); - } + for (Analyzer analyzer : ANALYZER) { + if (analyzer != null) { + ANALYZER_RESPONSES.add(analyzer.analyze(throwable, messageFormat, args)); } } - for (Map currentProviderResponse : providerResponse) { - if (currentProviderResponse != null && !currentProviderResponse.isEmpty()) { + for (Map response : PROVIDER_RESPONSES) { + if (response != null && !response.isEmpty()) { addSeparator(output); - for (Map.Entry entry : currentProviderResponse.entrySet()) { + for (Map.Entry entry : response.entrySet()) { output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } } - for (String currentPrompt : analyzerResponse) { - if (currentPrompt != null && !currentPrompt.isEmpty()) { + for (String response : ANALYZER_RESPONSES) { + if (response != null && !response.isEmpty()) { addSeparator(output); - output.append(currentPrompt).append("\n"); + output.append(response).append("\n"); } } @@ -72,8 +69,8 @@ public class CrashReportGenerator { sink.append("Null"); } - logger.info("\n" + output.toString()); - logger.fatal("Stacktrace: \n" + sink.toString()); + LOGGER.info(output.toString()); + LOGGER.fatal("Stacktrace: \n" + sink.toString()); addSeparator(output); @@ -86,11 +83,9 @@ public class CrashReportGenerator { // PLAK } - createFileForCrashReport(output); createFileForLatestCrashReport(output); - System.exit(0); } @@ -111,7 +106,7 @@ public class CrashReportGenerator { } public static void createFileForLatestCrashReport(StringBuilder sb) { - try (FileOutputStream fos = new FileOutputStream(latestLogFile)) { + try (FileOutputStream fos = new FileOutputStream(LATEST_LOG_FILE)) { byte[] buffer = sb.toString().getBytes(); fos.write(buffer, 0, buffer.length); @@ -121,11 +116,11 @@ public class CrashReportGenerator { } public static void registerProvider(ContextProvider provider) { - providers.add(provider); + PROVIDERS.add(provider); } public static void registerAnalyzer(Analyzer analyzer) { - analyzers.add(analyzer); + ANALYZER.add(analyzer); } private static void addSeparator(StringBuilder sb) { diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java index e750cea..d5fb6dc 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java @@ -4,7 +4,7 @@ import ru.windcorp.progressia.common.util.crash.Analyzer; public class OutOfMemoryAnalyzer implements Analyzer { @Override - public String getPrompt(Throwable throwable, String messageFormat, Object... args) { + public String analyze(Throwable throwable, String messageFormat, Object... args) { if (throwable instanceof OutOfMemoryError) return "Try add memory for the JVM"; return null; diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java index 712ee17..56b8916 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java @@ -9,10 +9,10 @@ public class OSContextProvider implements ContextProvider { @Override public Map provideContext() { - Map theThings = new HashMap<>(); - theThings.put("Name OS", System.getProperty("os.name")); - theThings.put("Version OS", System.getProperty("os.version")); - theThings.put("Architecture OS", System.getProperty("os.arch")); - return theThings; + Map result = new HashMap<>(); + result.put("Name OS", System.getProperty("os.name")); + result.put("Version OS", System.getProperty("os.version")); + result.put("Architecture OS", System.getProperty("os.arch")); + return result; } } From ddf48c058709023acc295061388c8397f40fcc4e Mon Sep 17 00:00:00 2001 From: serega404 Date: Wed, 4 Nov 2020 23:10:07 +0300 Subject: [PATCH 03/24] Crash-reports code update --- .../common/util/crash/Analyzer.java | 2 + .../common/util/crash/ContextProvider.java | 4 +- .../util/crash/CrashReportGenerator.java | 124 +++++++++++------- .../crash/analyzers/OutOfMemoryAnalyzer.java | 5 + .../crash/providers/OSContextProvider.java | 16 ++- 5 files changed, 95 insertions(+), 56 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java index 0afdb1d..e98814d 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java @@ -2,4 +2,6 @@ package ru.windcorp.progressia.common.util.crash; public interface Analyzer { String analyze(Throwable throwable, String messageFormat, Object... args); + + String getName(); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java index 06150f0..a1077e8 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java @@ -3,5 +3,7 @@ package ru.windcorp.progressia.common.util.crash; import java.util.Map; public interface ContextProvider { - Map provideContext(); + void provideContext(Map output); + + String getName(); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java index c51c125..7720efc 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -3,25 +3,28 @@ package ru.windcorp.progressia.common.util.crash; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.*; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.Map; +import java.util.*; public class CrashReportGenerator { - private CrashReportGenerator() {} + private CrashReportGenerator() { + } - private static final File LATEST_LOG_FILE = new File("crash-reports/latest.log"); + private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); private static final Collection PROVIDERS = new ArrayList<>(); - private static final Collection> PROVIDER_RESPONSES = new ArrayList<>(); - private static final Collection ANALYZER = new ArrayList<>(); - private static final Collection ANALYZER_RESPONSES = new ArrayList<>(); + private static final Collection ANALYZERS = new ArrayList<>(); private static final Logger LOGGER = LogManager.getLogger("crash"); @@ -31,32 +34,56 @@ public class CrashReportGenerator { for (ContextProvider provider : PROVIDERS) { if (provider != null) { - PROVIDER_RESPONSES.add(provider.provideContext()); - } - } + Map buf = new HashMap<>(); - for (Analyzer analyzer : ANALYZER) { - if (analyzer != null) { - ANALYZER_RESPONSES.add(analyzer.analyze(throwable, messageFormat, args)); - } - } + try { + provider.provideContext(buf); - for (Map response : PROVIDER_RESPONSES) { - if (response != null && !response.isEmpty()) { - addSeparator(output); - for (Map.Entry entry : response.entrySet()) { - output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + addSeparator(output); + output.append("Provider name: ").append(provider.getName()).append("\n"); + for (Map.Entry entry : buf.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + } catch (Throwable t) { + try { + addSeparator(output); + output.append(provider.getName()).append(" is broken").append("\n"); + } catch (Throwable th) { + // You stupid + } + // Analyzer is broken } } } - for (String response : ANALYZER_RESPONSES) { - if (response != null && !response.isEmpty()) { - addSeparator(output); - output.append(response).append("\n"); + addSeparator(output); + + boolean analyzerResponseExist = false; + for (Analyzer analyzer : ANALYZERS) { + if (analyzer != null) { + + String answer; + try { + answer = analyzer.analyze(throwable, messageFormat, args); + + if (answer != null && !answer.isEmpty()) { + analyzerResponseExist = true; + output.append(analyzer.getName()).append(": ").append(answer).append("\n"); + } + } catch (Throwable t) { + try { + analyzerResponseExist = true; + output.append(analyzer.getName()).append(" is broken").append("\n"); + } catch (Throwable th) { + // You stupid + } + // Analyzer is broken + } } } + if (analyzerResponseExist) addSeparator(output); + // Formatting to a human-readable string StringWriter sink = new StringWriter(); if (throwable != null) { @@ -69,47 +96,48 @@ public class CrashReportGenerator { sink.append("Null"); } - LOGGER.info(output.toString()); - LOGGER.fatal("Stacktrace: \n" + sink.toString()); - - addSeparator(output); - output.append("Stacktrace: \n"); output.append(sink.toString()).append("\n"); + LOGGER.fatal("/n" + output.toString()); + try { System.err.println(output.toString()); } catch (Exception e) { // PLAK } - createFileForCrashReport(output); - createFileForLatestCrashReport(output); + generateCrashReportFiles(output.toString()); System.exit(0); } - public static void createFileForCrashReport(StringBuilder sb) { + public static void generateCrashReportFiles(String output) { Date date = new Date(); - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); - File logFile = new File("crash-reports/" + "crash-" + dateFormat.format(date) + ".log"); + boolean pathExist = false; + if (!Files.exists(CRASH_REPORTS_PATH)) { - try (FileOutputStream fos = new FileOutputStream(logFile)) { - byte[] buffer = sb.toString().getBytes(); + try { + Files.createDirectory(CRASH_REPORTS_PATH); + ; + pathExist = true; + } catch (IOException e) { + // Crash Report not created + } - fos.write(buffer, 0, buffer.length); - } catch (IOException ex) { - // Crash Report not created + } else pathExist = true; + + if (pathExist) { + createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); + createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); } } - public static void createFileForLatestCrashReport(StringBuilder sb) { - try (FileOutputStream fos = new FileOutputStream(LATEST_LOG_FILE)) { - byte[] buffer = sb.toString().getBytes(); - - fos.write(buffer, 0, buffer.length); + public static void createFileForCrashReport(String buffer, String filename) { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) { + writer.write(buffer); } catch (IOException ex) { // Crash Report not created } @@ -120,7 +148,7 @@ public class CrashReportGenerator { } public static void registerAnalyzer(Analyzer analyzer) { - ANALYZER.add(analyzer); + ANALYZERS.add(analyzer); } private static void addSeparator(StringBuilder sb) { diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java index d5fb6dc..85a41e4 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java @@ -9,4 +9,9 @@ public class OutOfMemoryAnalyzer implements Analyzer { return "Try add memory for the JVM"; return null; } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java index 56b8916..ecfe6ab 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java @@ -2,17 +2,19 @@ package ru.windcorp.progressia.common.util.crash.providers; import ru.windcorp.progressia.common.util.crash.ContextProvider; -import java.util.HashMap; import java.util.Map; public class OSContextProvider implements ContextProvider { @Override - public Map provideContext() { - Map result = new HashMap<>(); - result.put("Name OS", System.getProperty("os.name")); - result.put("Version OS", System.getProperty("os.version")); - result.put("Architecture OS", System.getProperty("os.arch")); - return result; + public void provideContext(Map output) { + output.put("Name OS", System.getProperty("os.name")); + output.put("Version OS", System.getProperty("os.version")); + output.put("Architecture OS", System.getProperty("os.arch")); + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); } } From 044c690d096c944a0807f97a2c7ed6deb1bddb88 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 5 Nov 2020 12:14:03 +0300 Subject: [PATCH 04/24] Code Review - Renamed CrashReportGenerator.makeCrashReport to .crash for convenience - Split .crash into subroutines - Documented Analyzer and ContextProvider - Both are now supposed to have names In Title Case - CRG.export now assumes PrintStream does not fail and assumes Log can fail --- .../client/ProgressiaClientMain.java | 2 +- .../common/util/crash/Analyzer.java | 25 ++++ .../common/util/crash/ContextProvider.java | 22 ++++ .../util/crash/CrashReportGenerator.java | 117 ++++++++++++------ .../crash/analyzers/OutOfMemoryAnalyzer.java | 4 +- .../crash/providers/OSContextProvider.java | 8 +- 6 files changed, 130 insertions(+), 48 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java index 84fa145..bc79991 100644 --- a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java +++ b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java @@ -38,7 +38,7 @@ public class ProgressiaClientMain { long[] ssdss = new long[1 << 30]; } catch (Throwable t) { - CrashReportGenerator.makeCrashReport(t, "u %s stupid", "vry"); + CrashReportGenerator.crash(t, "u %s stupid", "vry"); } ProgressiaLauncher.launch(args, new ClientProxy()); diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java index e98814d..df9bdcd 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/Analyzer.java @@ -1,7 +1,32 @@ package ru.windcorp.progressia.common.util.crash; +/** + * A crash report utility that performs analysis of a problem during crash + * report generation and presents its conclusion to the user via the crash report. + * Unlike {@link ContextProvider}s, Analyzers are provided with the reported problem + * details. + * @see ContextProvider + * @author serega404 + */ public interface Analyzer { + + /** + * Provides a human-readable string describing this analyzer's conclusion + * on the presented problem, or returns {@code null} if no conclusion + * could be made. + * @param throwable The reported throwable (may be {@code null}) + * @param messageFormat A {@linkplain java.util.Formatter#syntax format string} of a + * human-readable description of the problem + * @param args The arguments for the format string + * @return a conclusion or {@code null} + */ String analyze(Throwable throwable, String messageFormat, Object... args); + /** + * Returns this analyzer's human-readable name. + * It should be A String In Title Case With Spaces. + * @return this analyzer's name + */ String getName(); + } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java index a1077e8..7938c2a 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/ContextProvider.java @@ -2,8 +2,30 @@ package ru.windcorp.progressia.common.util.crash; import java.util.Map; +/** + * A crash report utility that gathers information about game and system state + * when a crash occurs and presents it to the user via the crash report. + * ContextProviders are not aware of the nature of the problem, unlike {@link Analyzer}s. + * @see Analyzer + * @author serega404 + */ public interface ContextProvider { + + /** + * Provides human-readable description of the state of the game and the system. + * This information is {@link Map#put(Object, Object) put} into the provided map + * as key-value pairs. Keys are the characteristic being described, such as "OS Name", + * and should be Strings In Title Case With Spaces. + * If this provider cannot provide any information at this moment, the map is not + * modified. + * @param output the map to append output to + */ void provideContext(Map output); + /** + * Returns this provider's human-readable name. + * It should be A String In Title Case With Spaces. + * @return this provider's name + */ String getName(); } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java index 7720efc..ca5311f 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -2,11 +2,12 @@ package ru.windcorp.progressia.common.util.crash; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.util.StringBuilderWriter; import java.io.BufferedWriter; import java.io.IOException; import java.io.PrintWriter; -import java.io.StringWriter; +import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -17,21 +18,43 @@ import java.util.*; public class CrashReportGenerator { - private CrashReportGenerator() { - } + private CrashReportGenerator() {} private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); - private static final Collection PROVIDERS = new ArrayList<>(); + private static final Collection PROVIDERS = + Collections.synchronizedCollection(new ArrayList<>()); - private static final Collection ANALYZERS = new ArrayList<>(); + private static final Collection ANALYZERS = + Collections.synchronizedCollection(new ArrayList<>()); private static final Logger LOGGER = LogManager.getLogger("crash"); - public static void makeCrashReport(Throwable throwable, String messageFormat, Object... args) { - + /** + * This method never returns. + *

+ * TODO document + * @param throwable + * @param messageFormat + * @param args + */ + public static void crash(Throwable throwable, String messageFormat, Object... args) { StringBuilder output = new StringBuilder(); + appendContextProviders(output); + addSeparator(output); + if (appendAnalyzers(output, throwable, messageFormat, args)) { + addSeparator(output); + } + + appendStackTrace(output, throwable); + + export(output.toString()); + + System.exit(0); + } + + private static void appendContextProviders(StringBuilder output) { for (ContextProvider provider : PROVIDERS) { if (provider != null) { Map buf = new HashMap<>(); @@ -55,10 +78,13 @@ public class CrashReportGenerator { } } } - - addSeparator(output); - - boolean analyzerResponseExist = false; + } + + private static boolean appendAnalyzers( + StringBuilder output, + Throwable throwable, String messageFormat, Object[] args + ) { + boolean analyzerResponsesExist = false; for (Analyzer analyzer : ANALYZERS) { if (analyzer != null) { @@ -67,12 +93,12 @@ public class CrashReportGenerator { answer = analyzer.analyze(throwable, messageFormat, args); if (answer != null && !answer.isEmpty()) { - analyzerResponseExist = true; + analyzerResponsesExist = true; output.append(analyzer.getName()).append(": ").append(answer).append("\n"); } } catch (Throwable t) { try { - analyzerResponseExist = true; + analyzerResponsesExist = true; output.append(analyzer.getName()).append(" is broken").append("\n"); } catch (Throwable th) { // You stupid @@ -81,38 +107,40 @@ public class CrashReportGenerator { } } } + + return analyzerResponsesExist; + } - if (analyzerResponseExist) addSeparator(output); - - // Formatting to a human-readable string - StringWriter sink = new StringWriter(); - if (throwable != null) { - try { - throwable.printStackTrace(new PrintWriter(sink)); - } catch (Exception e) { - // PLAK - } - } else { - sink.append("Null"); - } - + private static void appendStackTrace(StringBuilder output, Throwable throwable) { output.append("Stacktrace: \n"); - output.append(sink.toString()).append("\n"); - - LOGGER.fatal("/n" + output.toString()); - + + if (throwable == null) { + output.append("no Throwable provided").append("\n"); + } + + // Formatting to a human-readable string + Writer sink = new StringBuilderWriter(output); try { - System.err.println(output.toString()); + throwable.printStackTrace(new PrintWriter(sink)); } catch (Exception e) { // PLAK } - - generateCrashReportFiles(output.toString()); - - System.exit(0); + output.append("\n"); + } + + private static void export(String report) { + try { + LOGGER.fatal("/n" + report); + } catch (Exception e) { + // PLAK + } + + System.err.println(report); + + generateCrashReportFiles(report); } - public static void generateCrashReportFiles(String output) { + private static void generateCrashReportFiles(String output) { Date date = new Date(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); @@ -121,7 +149,6 @@ public class CrashReportGenerator { try { Files.createDirectory(CRASH_REPORTS_PATH); - ; pathExist = true; } catch (IOException e) { // Crash Report not created @@ -135,8 +162,13 @@ public class CrashReportGenerator { } } - public static void createFileForCrashReport(String buffer, String filename) { - try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) { + private static void createFileForCrashReport(String buffer, String filename) { + try ( + BufferedWriter writer = Files.newBufferedWriter( + Paths.get(filename), + StandardCharsets.UTF_8 + ) + ) { writer.write(buffer); } catch (IOException ex) { // Crash Report not created @@ -152,6 +184,9 @@ public class CrashReportGenerator { } private static void addSeparator(StringBuilder sb) { - sb.append("-------------------------------------------------").append("\n"); + sb.append( + // 80 chars + "--------------------------------------------------------------------------------" + ).append("\n"); } } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java index 85a41e4..95d5f76 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/analyzers/OutOfMemoryAnalyzer.java @@ -6,12 +6,12 @@ public class OutOfMemoryAnalyzer implements Analyzer { @Override public String analyze(Throwable throwable, String messageFormat, Object... args) { if (throwable instanceof OutOfMemoryError) - return "Try add memory for the JVM"; + return "Try to add memory to the JVM"; return null; } @Override public String getName() { - return this.getClass().getSimpleName(); + return "Out Of Memory Analyzer"; } } diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java index ecfe6ab..f8d4ab4 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/providers/OSContextProvider.java @@ -8,13 +8,13 @@ public class OSContextProvider implements ContextProvider { @Override public void provideContext(Map output) { - output.put("Name OS", System.getProperty("os.name")); - output.put("Version OS", System.getProperty("os.version")); - output.put("Architecture OS", System.getProperty("os.arch")); + output.put("OS Name", System.getProperty("os.name")); + output.put("OS Version", System.getProperty("os.version")); + output.put("OS Architecture", System.getProperty("os.arch")); } @Override public String getName() { - return this.getClass().getSimpleName(); + return "OS Context Provider"; } } From d5723b9ae6badf4b51e4293349cd01f0ae5ebe1c Mon Sep 17 00:00:00 2001 From: serega404 Date: Sat, 7 Nov 2020 21:52:09 +0300 Subject: [PATCH 05/24] Refactored crash-report --- .../util/crash/CrashReportGenerator.java | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java index ca5311f..0109bdd 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -46,9 +46,11 @@ public class CrashReportGenerator { if (appendAnalyzers(output, throwable, messageFormat, args)) { addSeparator(output); } - + + appendMessageFormat(output, messageFormat, args); + appendStackTrace(output, throwable); - + export(output.toString()); System.exit(0); @@ -62,16 +64,19 @@ public class CrashReportGenerator { try { provider.provideContext(buf); - addSeparator(output); - output.append("Provider name: ").append(provider.getName()).append("\n"); - for (Map.Entry entry : buf.entrySet()) { - output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + if (!buf.isEmpty()) { + addSeparator(output); + output.append("Provider name: ").append(provider.getName()).append("\n"); + for (Map.Entry entry : buf.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } } } catch (Throwable t) { + output.append("\n"); try { - addSeparator(output); output.append(provider.getName()).append(" is broken").append("\n"); } catch (Throwable th) { + output.append(provider.getClass().getName()).append(" is broken").append("\n"); // You stupid } // Analyzer is broken @@ -107,17 +112,24 @@ public class CrashReportGenerator { } } } - + return analyzerResponsesExist; } + private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { + output.append("Provided description: ").append(String.format(messageFormat, arg)).append("\n"); + + addSeparator(output); + } + private static void appendStackTrace(StringBuilder output, Throwable throwable) { output.append("Stacktrace: \n"); - + if (throwable == null) { output.append("no Throwable provided").append("\n"); + return; } - + // Formatting to a human-readable string Writer sink = new StringBuilderWriter(output); try { @@ -127,16 +139,16 @@ public class CrashReportGenerator { } output.append("\n"); } - + private static void export(String report) { try { LOGGER.fatal("/n" + report); } catch (Exception e) { // PLAK } - + System.err.println(report); - + generateCrashReportFiles(report); } @@ -144,21 +156,13 @@ public class CrashReportGenerator { Date date = new Date(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); - boolean pathExist = false; - if (!Files.exists(CRASH_REPORTS_PATH)) { + try { + if (!Files.exists(CRASH_REPORTS_PATH)) Files.createDirectory(CRASH_REPORTS_PATH); - try { - Files.createDirectory(CRASH_REPORTS_PATH); - pathExist = true; - } catch (IOException e) { - // Crash Report not created - } - - } else pathExist = true; - - if (pathExist) { createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); + } catch (Throwable t) { + // Crash Report not created } } From 0017ecc5a74297204938d8adfc1e3d65b6088616 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 10 Nov 2020 01:25:42 +0300 Subject: [PATCH 06/24] Refactored CrashReportGenerator to avoid deadlocks and clarify code --- .../util/crash/CrashReportGenerator.java | 96 +++++++++++-------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java index 0109bdd..18bacba 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReportGenerator.java @@ -57,30 +57,39 @@ public class CrashReportGenerator { } private static void appendContextProviders(StringBuilder output) { - for (ContextProvider provider : PROVIDERS) { - if (provider != null) { + + // Do a local copy to avoid deadlocks -OLEGSHA + ContextProvider[] localProvidersCopy = + PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); + + for (ContextProvider provider : localProvidersCopy) { + if (provider == null) continue; + + addSeparator(output); + + try { Map buf = new HashMap<>(); + provider.provideContext(buf); - try { - provider.provideContext(buf); - - if (!buf.isEmpty()) { - addSeparator(output); - output.append("Provider name: ").append(provider.getName()).append("\n"); - for (Map.Entry entry : buf.entrySet()) { - output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); - } + if (!buf.isEmpty()) { + output.append("Provider name: ").append(provider.getName()).append("\n"); + for (Map.Entry entry : buf.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } - } catch (Throwable t) { - output.append("\n"); - try { - output.append(provider.getName()).append(" is broken").append("\n"); - } catch (Throwable th) { - output.append(provider.getClass().getName()).append(" is broken").append("\n"); - // You stupid - } - // Analyzer is broken } + } catch (Throwable t) { + output.append("\n"); + + String providerName; + + try { + providerName = provider.getName(); + } catch (Throwable t1) { + providerName = provider.getClass().getName(); + } + + output.append(providerName).append(" is broken").append("\n"); + // ContextProvider is broken } } } @@ -90,26 +99,37 @@ public class CrashReportGenerator { Throwable throwable, String messageFormat, Object[] args ) { boolean analyzerResponsesExist = false; - for (Analyzer analyzer : ANALYZERS) { - if (analyzer != null) { + + // Do a local copy to avoid deadlocks -OLEGSHA + Analyzer[] localAnalyzersCopy = + ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); + + for (Analyzer analyzer : localAnalyzersCopy) { + if (analyzer == null) continue; - String answer; - try { - answer = analyzer.analyze(throwable, messageFormat, args); + String answer; + try { + answer = analyzer.analyze(throwable, messageFormat, args); - if (answer != null && !answer.isEmpty()) { - analyzerResponsesExist = true; - output.append(analyzer.getName()).append(": ").append(answer).append("\n"); - } - } catch (Throwable t) { - try { - analyzerResponsesExist = true; - output.append(analyzer.getName()).append(" is broken").append("\n"); - } catch (Throwable th) { - // You stupid - } - // Analyzer is broken + if (answer != null && !answer.isEmpty()) { + analyzerResponsesExist = true; + output.append(analyzer.getName()).append(": ").append(answer).append("\n"); } + } catch (Throwable t) { + analyzerResponsesExist = true; + + output.append("\n"); + + String analyzerName; + + try { + analyzerName = analyzer.getName(); + } catch (Throwable t1) { + analyzerName = analyzer.getClass().getName(); + } + + output.append(analyzerName).append(" is broken").append("\n"); + // Analyzer is broken } } @@ -117,7 +137,7 @@ public class CrashReportGenerator { } private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { - output.append("Provided description: ").append(String.format(messageFormat, arg)).append("\n"); + output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n"); addSeparator(output); } From 0dd5e6d3da02ca3922d28624017bd92e246796e4 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 12 Nov 2020 23:13:41 +0300 Subject: [PATCH 07/24] Increased encapsulation of AABBs and added TranslatedAABB - Interface of AABB (getOrigin(), getSize() and getWall(int)) now extracted into AABBoid interface - Same with Walls - Collection getWalls() replaced with Wall getWall(int) - Added TranslatedAABB extends AABBoid - CollisionClock replaced with direct reference to WorldData - Reorganized tmp code in LayerWorld, so as to decrease cRiNgE_ levels --- .../client/graphics/world/LayerWorld.java | 72 +++++------ .../progressia/common/collision/AABB.java | 112 ++++++++++-------- .../progressia/common/collision/AABBoid.java | 17 +++ .../common/collision/CollisionClock.java | 8 -- .../common/collision/CollisionWall.java | 55 --------- .../common/collision/TranslatedAABB.java | 105 ++++++++++++++++ .../progressia/common/collision/Wall.java | 12 ++ ...AABBCollider.java => AABBoidCollider.java} | 60 ++++++---- .../common/collision/colliders/Collider.java | 45 +++---- .../progressia/common/util/Vectors.java | 13 ++ .../progressia/common/world/WorldData.java | 14 +++ .../progressia/test/AABBRenderer.java | 35 ------ .../test/CollisionModelRenderer.java | 61 ++++++++++ .../progressia/test/TestEntityDataStatie.java | 3 +- 14 files changed, 383 insertions(+), 229 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/collision/AABBoid.java delete mode 100644 src/main/java/ru/windcorp/progressia/common/collision/CollisionClock.java delete mode 100644 src/main/java/ru/windcorp/progressia/common/collision/CollisionWall.java create mode 100644 src/main/java/ru/windcorp/progressia/common/collision/TranslatedAABB.java create mode 100644 src/main/java/ru/windcorp/progressia/common/collision/Wall.java rename src/main/java/ru/windcorp/progressia/common/collision/colliders/{AABBWithAABBCollider.java => AABBoidCollider.java} (78%) delete mode 100644 src/main/java/ru/windcorp/progressia/test/AABBRenderer.java create mode 100644 src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 114e1ed..5f74963 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -36,16 +36,12 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.bus.Input; -import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.Collideable; -import ru.windcorp.progressia.common.collision.CollisionClock; -import ru.windcorp.progressia.common.collision.CollisionModel; -import ru.windcorp.progressia.common.collision.CompoundCollisionModel; import ru.windcorp.progressia.common.collision.colliders.Collider; import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.test.AABBRenderer; +import ru.windcorp.progressia.test.CollisionModelRenderer; public class LayerWorld extends Layer { @@ -128,52 +124,46 @@ public class LayerWorld extends Layer { private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace(); private final List tmp_collideableList = new ArrayList<>(); - private static final boolean RENDER_AABBS = true; + private static final boolean RENDER_COLLISION_MODELS = true; private void tmp_doEveryFrame() { try { - if (RENDER_AABBS) { - for (EntityData data : this.client.getWorld().getData().getEntities()) { - CollisionModel model = data.getCollisionModel(); - if (model instanceof AABB) { - AABBRenderer.renderAABB((AABB) model, helper); - } else if (model instanceof CompoundCollisionModel) { - AABBRenderer.renderAABBsInCompound((CompoundCollisionModel) model, helper); - } - } - } - - tmp_collideableList.clear(); - tmp_collideableList.addAll(this.client.getWorld().getData().getEntities()); - - Collider.performCollisions( - tmp_collideableList, - new CollisionClock() { - private float t = 0; - @Override - public float getTime() { - return t; - } - - @Override - public void advanceTime(float change) { - t += change; - } - }, - (float) GraphicsInterface.getFrameLength(), - tmp_colliderWorkspace - ); - - final float frictionCoeff = 1 - 1e-2f; + tmp_performCollisions(); for (EntityData data : this.client.getWorld().getData().getEntities()) { - data.getVelocity().mul(frictionCoeff); + tmp_applyFriction(data); + tmp_renderCollisionModel(data); } - } catch (Exception e) { + } catch (Throwable e) { + e.printStackTrace(); + System.out.println("OLEGSHA is to blame. Tell him he vry stupiDD!!"); System.exit(31337); } } + private void tmp_renderCollisionModel(EntityData entity) { + if (RENDER_COLLISION_MODELS) { + CollisionModelRenderer.renderCollisionModel(entity.getCollisionModel(), helper); + } + } + + private void tmp_performCollisions() { + tmp_collideableList.clear(); + tmp_collideableList.addAll(this.client.getWorld().getData().getEntities()); + + Collider.performCollisions( + tmp_collideableList, + this.client.getWorld().getData(), + (float) GraphicsInterface.getFrameLength(), + tmp_colliderWorkspace + ); + } + + private void tmp_applyFriction(EntityData entity) { + final float frictionCoeff = 1 - 1e-2f; + entity.getVelocity().mul(frictionCoeff); + } + @Override protected void handleInput(Input input) { if (input.isConsumed()) return; diff --git a/src/main/java/ru/windcorp/progressia/common/collision/AABB.java b/src/main/java/ru/windcorp/progressia/common/collision/AABB.java index 7188e33..9b0fd31 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/AABB.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/AABB.java @@ -1,93 +1,111 @@ package ru.windcorp.progressia.common.collision; -import java.util.Collection; -import java.util.Map; - import glm.vec._3.Vec3; -import ru.windcorp.progressia.common.world.block.BlockFace; -public class AABB implements CollisionModel { +/** + * An implementation of an + * Axis-Aligned Bounding Box. + * @author javapony + */ +public class AABB implements AABBoid { + + private class AABBWallImpl implements Wall { + + private final Vec3 originOffset = new Vec3(); + private final Vec3 widthSelector = new Vec3(); + private final Vec3 heightSelector = new Vec3(); - private final Map faces = BlockFace.mapToFaces( - new CollisionWall(-0.5f, -0.5f, -0.5f, +1, 0, 0, 0, 0, +1), - new CollisionWall(+0.5f, -0.5f, -0.5f, 0, +1, 0, 0, 0, +1), - new CollisionWall(+0.5f, +0.5f, -0.5f, -1, 0, 0, 0, 0, +1), - new CollisionWall(-0.5f, +0.5f, -0.5f, 0, -1, 0, 0, 0, +1), - - new CollisionWall(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0), - new CollisionWall(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0) - ); + public AABBWallImpl( + float ox, float oy, float oz, + float wx, float wy, float wz, + float hx, float hy, float hz + ) { + this.originOffset.set(ox, oy, oz); + this.widthSelector.set(wx, wy, wz); + this.heightSelector.set(hx, hy, hz); + } + + @Override + public void getOrigin(Vec3 output) { + output.set(originOffset).mul(AABB.this.getSize()).add(AABB.this.getOrigin()); + } + + @Override + public void getWidth(Vec3 output) { + output.set(AABB.this.getSize()).mul(widthSelector); + } + + @Override + public void getHeight(Vec3 output) { + output.set(AABB.this.getSize()).mul(heightSelector); + } + + } + + private final Wall[] walls = new Wall[] { + new AABBWallImpl(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0), // Top + new AABBWallImpl(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0), // Bottom + new AABBWallImpl(+0.5f, -0.5f, -0.5f, 0, +1, 0, 0, 0, +1), // North + new AABBWallImpl(-0.5f, +0.5f, -0.5f, 0, -1, 0, 0, 0, +1), // South + new AABBWallImpl(+0.5f, +0.5f, -0.5f, -1, 0, 0, 0, 0, +1), // West + new AABBWallImpl(-0.5f, -0.5f, -0.5f, +1, 0, 0, 0, 0, +1) // East + }; private final Vec3 origin = new Vec3(); private final Vec3 size = new Vec3(); public AABB(Vec3 origin, Vec3 size) { - this.origin.set(origin); - this.size.set(size); - - for (CollisionWall wall : getFaces()) { - wall.moveOrigin(origin); - wall.getWidth().mul(size); - wall.getHeight().mul(size); - } + this(origin.x, origin.y, origin.z, size.x, size.y, size.z); } public AABB( - float ox, float oy, float oz, + float ox, float oy, float oz, float xSize, float ySize, float zSize ) { this.origin.set(ox, oy, oz); this.size.set(xSize, ySize, zSize); - - for (CollisionWall wall : getFaces()) { - wall.moveOrigin(ox, oy, oz); - wall.getWidth().mul(xSize, ySize, zSize); - wall.getHeight().mul(xSize, ySize, zSize); - } - } - - public Collection getFaces() { - return faces.values(); } public Vec3 getOrigin() { return origin; } + + @Override + public void getOrigin(Vec3 output) { + output.set(origin); + } @Override public void setOrigin(Vec3 origin) { - for (CollisionWall wall : getFaces()) { - wall.getOrigin().sub(this.origin).add(origin); - } - this.origin.set(origin); } @Override public void moveOrigin(Vec3 displacement) { - for (CollisionWall wall : getFaces()) { - wall.getOrigin().add(displacement); - } - this.origin.add(displacement); } public Vec3 getSize() { return size; } + + @Override + public void getSize(Vec3 output) { + output.set(size); + } public void setSize(Vec3 size) { setSize(size.x, size.y, size.z); } public void setSize(float xSize, float ySize, float zSize) { - for (CollisionWall wall : getFaces()) { - wall.getWidth().div(this.size).mul(xSize, ySize, zSize); - wall.getHeight().div(this.size).mul(xSize, ySize, zSize); - wall.getOrigin().sub(getOrigin()).div(this.size).mul(xSize, ySize, zSize).add(getOrigin()); - } - this.size.set(xSize, ySize, zSize); } + + @Override + public Wall getWall(int faceId) { + // No, we don't support Apple. + return walls[faceId]; + } } diff --git a/src/main/java/ru/windcorp/progressia/common/collision/AABBoid.java b/src/main/java/ru/windcorp/progressia/common/collision/AABBoid.java new file mode 100644 index 0000000..6e1d384 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/collision/AABBoid.java @@ -0,0 +1,17 @@ +package ru.windcorp.progressia.common.collision; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public interface AABBoid extends CollisionModel { + + void getOrigin(Vec3 output); + void getSize(Vec3 output); + + default Wall getWall(BlockFace face) { + return getWall(face.getId()); + } + + Wall getWall(int faceId); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/CollisionClock.java b/src/main/java/ru/windcorp/progressia/common/collision/CollisionClock.java deleted file mode 100644 index cf42614..0000000 --- a/src/main/java/ru/windcorp/progressia/common/collision/CollisionClock.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.windcorp.progressia.common.collision; - -public interface CollisionClock { - - float getTime(); - void advanceTime(float change); - -} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/CollisionWall.java b/src/main/java/ru/windcorp/progressia/common/collision/CollisionWall.java deleted file mode 100644 index 8963ffd..0000000 --- a/src/main/java/ru/windcorp/progressia/common/collision/CollisionWall.java +++ /dev/null @@ -1,55 +0,0 @@ -package ru.windcorp.progressia.common.collision; - -import glm.vec._3.Vec3; - -public class CollisionWall { - - private final Vec3 origin = new Vec3(); - private final Vec3 width = new Vec3(); - private final Vec3 height = new Vec3(); - - public CollisionWall(Vec3 origin, Vec3 width, Vec3 height) { - this.origin.set(origin); - this.width.set(width); - this.height.set(height); - } - - public CollisionWall( - float ox, float oy, float oz, - float wx, float wy, float wz, - float hx, float hy, float hz - ) { - this.origin.set(ox, oy, oz); - this.width.set(wx, wy, wz); - this.height.set(hx, hy, hz); - } - - public Vec3 getOrigin() { - return origin; - } - - public Vec3 getWidth() { - return width; - } - - public Vec3 getHeight() { - return height; - } - - public void setOrigin(Vec3 origin) { - setOrigin(origin.x, origin.y, origin.z); - } - - public void setOrigin(float x, float y, float z) { - this.origin.set(x, y, z); - } - - public void moveOrigin(Vec3 displacement) { - moveOrigin(displacement.x, displacement.y, displacement.z); - } - - public void moveOrigin(float dx, float dy, float dz) { - this.origin.add(dx, dy, dz); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/TranslatedAABB.java b/src/main/java/ru/windcorp/progressia/common/collision/TranslatedAABB.java new file mode 100644 index 0000000..a22c330 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/collision/TranslatedAABB.java @@ -0,0 +1,105 @@ +package ru.windcorp.progressia.common.collision; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class TranslatedAABB implements AABBoid { + + private class TranslatedAABBWall implements Wall { + private final int id; + + public TranslatedAABBWall(int id) { + this.id = id; + } + + @Override + public void getOrigin(Vec3 output) { + parent.getWall(id).getOrigin(output); + output.add(translation); + } + + @Override + public void getWidth(Vec3 output) { + parent.getWall(id).getWidth(output); + } + + @Override + public void getHeight(Vec3 output) { + parent.getWall(id).getHeight(output); + } + } + + private AABBoid parent; + private final Vec3 translation = new Vec3(); + + private final TranslatedAABBWall[] walls = new TranslatedAABBWall[BlockFace.BLOCK_FACE_COUNT]; + + { + for (int id = 0; id < walls.length; ++id) { + walls[id] = new TranslatedAABBWall(id); + } + } + + public TranslatedAABB(AABBoid parent, float tx, float ty, float tz) { + setParent(parent); + setTranslation(tx, ty, tz); + } + + public TranslatedAABB(AABBoid parent, Vec3 translation) { + this(parent, translation.x, translation.y, translation.z); + } + + public TranslatedAABB() { + this(null, 0, 0, 0); + } + + @Override + public void setOrigin(Vec3 origin) { + Vec3 v = Vectors.grab3().set(origin).sub(translation); + parent.setOrigin(v); + Vectors.release(v); + } + + @Override + public void moveOrigin(Vec3 displacement) { + parent.moveOrigin(displacement); + } + + @Override + public void getOrigin(Vec3 output) { + parent.getOrigin(output); + output.add(translation); + } + + @Override + public void getSize(Vec3 output) { + parent.getSize(output); + } + + @Override + public Wall getWall(int faceId) { + return walls[faceId]; + } + + public AABBoid getParent() { + return parent; + } + + public void setParent(AABBoid parent) { + this.parent = parent; + } + + public Vec3 getTranslation() { + return translation; + } + + public void setTranslation(Vec3 translation) { + setTranslation(translation.x, translation.y, translation.z); + } + + public void setTranslation(float tx, float ty, float tz) { + this.translation.set(tx, ty, tz); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/Wall.java b/src/main/java/ru/windcorp/progressia/common/collision/Wall.java new file mode 100644 index 0000000..9549f04 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/collision/Wall.java @@ -0,0 +1,12 @@ +package ru.windcorp.progressia.common.collision; + +import glm.vec._3.Vec3; + +public interface Wall { + + void getOrigin(Vec3 output); + + void getWidth(Vec3 output); + void getHeight(Vec3 output); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBWithAABBCollider.java b/src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBoidCollider.java similarity index 78% rename from src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBWithAABBCollider.java rename to src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBoidCollider.java index 7b961e1..f440761 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBWithAABBCollider.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/colliders/AABBoidCollider.java @@ -2,26 +2,25 @@ package ru.windcorp.progressia.common.collision.colliders; import glm.mat._3.Mat3; import glm.vec._3.Vec3; -import ru.windcorp.progressia.common.collision.AABB; -import ru.windcorp.progressia.common.collision.Collideable; -import ru.windcorp.progressia.common.collision.CollisionWall; +import ru.windcorp.progressia.common.collision.*; import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace; import ru.windcorp.progressia.common.collision.colliders.Collider.Collision; import ru.windcorp.progressia.common.util.Matrices; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.block.BlockFace; -class AABBWithAABBCollider { +class AABBoidCollider { static Collider.Collision computeModelCollision( Collideable aBody, Collideable bBody, - AABB aModel, AABB bModel, + AABBoid aModel, AABBoid bModel, float tickLength, ColliderWorkspace workspace ) { Collideable obstacleBody = bBody; Collideable colliderBody = aBody; - AABB obstacleModel = bModel; - AABB colliderModel = aModel; + AABBoid obstacleModel = bModel; + AABBoid colliderModel = aModel; Collision result = null; @@ -32,7 +31,8 @@ class AABBWithAABBCollider { computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody); // For every wall of collision space - for (CollisionWall wall : originCollisionSpace.getFaces()) { + for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) { + Wall wall = originCollisionSpace.getWall(i); Collision collision = computeWallCollision( wall, colliderModel, @@ -80,12 +80,21 @@ class AABBWithAABBCollider { Vectors.release(colliderVelocity); } - private static AABB createOriginCollisionSpace(AABB obstacle, AABB collider, AABB output) { - output.setOrigin(obstacle.getOrigin()); + private static AABB createOriginCollisionSpace(AABBoid obstacle, AABBoid collider, AABB output) { + Vec3 obstacleOrigin = Vectors.grab3(); + Vec3 obstacleSize = Vectors.grab3(); + Vec3 colliderSize = Vectors.grab3(); - Vec3 size = Vectors.grab3().set(obstacle.getSize()).add(collider.getSize()); - output.setSize(size); - Vectors.release(size); + obstacle.getOrigin(obstacleOrigin); + output.setOrigin(obstacleOrigin); + + obstacle.getSize(obstacleSize); + collider.getSize(colliderSize); + output.setSize(obstacleSize.add(colliderSize)); + + Vectors.release(obstacleOrigin); + Vectors.release(obstacleSize); + Vectors.release(colliderSize); return output; } @@ -134,27 +143,34 @@ class AABBWithAABBCollider { * If all conditions are satisfied, then the moment of impact is t0 + t. */ private static Collision computeWallCollision( - CollisionWall obstacleWall, - AABB colliderModel, + Wall obstacleWall, + AABBoid colliderModel, Vec3 collisionVelocity, float tickLength, ColliderWorkspace workspace, Collideable aBody, Collideable bBody ) { - Vec3 w = obstacleWall.getWidth(); - Vec3 h = obstacleWall.getHeight(); + Vec3 w = Vectors.grab3(); + Vec3 h = Vectors.grab3(); Vec3 v = Vectors.grab3(); Mat3 m = Matrices.grab3(); // The matrix [w h -v] Vec3 r = Vectors.grab3(); + Vec3 r_line = Vectors.grab3(); + Vec3 r_wall = Vectors.grab3(); Vec3 xyt = Vectors.grab3(); try { + obstacleWall.getWidth(w); + obstacleWall.getHeight(h); + v.set(collisionVelocity); if (isExiting(v, w, h)) { return null; } - r.set(colliderModel.getOrigin()).sub(obstacleWall.getOrigin()); + obstacleWall.getOrigin(r_wall); + colliderModel.getOrigin(r_line); + r.set(r_line).sub(r_wall); m.c0(w).c1(h).c2(v.negate()); if (Math.abs(m.det()) < 1e-6) { @@ -179,9 +195,13 @@ class AABBWithAABBCollider { return workspace.grab().set(aBody, bBody, obstacleWall, t); } finally { + Vectors.release(w); + Vectors.release(h); Vectors.release(v); - Vectors.release(r); Matrices.release(m); + Vectors.release(r); + Vectors.release(r_line); + Vectors.release(r_wall); Vectors.release(xyt); } } @@ -193,6 +213,6 @@ class AABBWithAABBCollider { return result; } - private AABBWithAABBCollider() {} + private AABBoidCollider() {} } diff --git a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java index 74bcc2c..2e6b591 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java @@ -6,14 +6,10 @@ import java.util.List; import org.apache.logging.log4j.LogManager; import glm.vec._3.Vec3; -import ru.windcorp.progressia.common.collision.AABB; -import ru.windcorp.progressia.common.collision.Collideable; -import ru.windcorp.progressia.common.collision.CollisionClock; -import ru.windcorp.progressia.common.collision.CollisionModel; -import ru.windcorp.progressia.common.collision.CollisionWall; -import ru.windcorp.progressia.common.collision.CompoundCollisionModel; +import ru.windcorp.progressia.common.collision.*; import ru.windcorp.progressia.common.util.LowOverheadCache; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.WorldData; public class Collider { @@ -21,7 +17,7 @@ public class Collider { public static void performCollisions( List colls, - CollisionClock clock, + WorldData world, float tickLength, ColliderWorkspace workspace ) { @@ -42,7 +38,7 @@ public class Collider { if (firstCollision == null) { break; } else { - collide(firstCollision, colls, clock, tickLength, workspace); + collide(firstCollision, colls, world, tickLength, workspace); workspace.release(firstCollision); collisionCount++; @@ -50,7 +46,7 @@ public class Collider { } } - advanceTime(colls, clock, tickLength); + advanceTime(colls, world, tickLength); } private static Collision getFirstCollision( @@ -108,10 +104,10 @@ public class Collider { float tickLength, ColliderWorkspace workspace ) { - if (aModel instanceof AABB && bModel instanceof AABB) { - return AABBWithAABBCollider.computeModelCollision( + if (aModel instanceof AABBoid && bModel instanceof AABBoid) { /*replace AABB with AABBoid where makes sense, also add TranslatedAABB support in TestAABBRenderer*/ + return AABBoidCollider.computeModelCollision( aBody, bBody, - (AABB) aModel, (AABB) bModel, + (AABBoid) aModel, (AABBoid) bModel, tickLength, workspace ); @@ -144,11 +140,11 @@ public class Collider { Collision collision, Collection colls, - CollisionClock clock, + WorldData world, float tickLength, ColliderWorkspace workspace ) { - advanceTime(colls, clock, collision.time); + advanceTime(colls, world, collision.time); boolean doNotHandle = false; @@ -237,7 +233,7 @@ public class Collider { Vec3 du_a = Vectors.grab3(); Vec3 du_b = Vectors.grab3(); - n.set(collision.wall.getWidth()).cross(collision.wall.getHeight()).normalize(); + n.set(collision.wallWidth).cross(collision.wallHeight).normalize(); collision.a.getCollideableVelocity(v_a); collision.b.getCollideableVelocity(v_b); @@ -306,10 +302,10 @@ public class Collider { private static void advanceTime( Collection colls, - CollisionClock clock, + WorldData world, float step ) { - clock.advanceTime(step); + world.advanceTime(step); Vec3 tmp = Vectors.grab3(); @@ -342,7 +338,9 @@ public class Collider { static class Collision { public Collideable a; public Collideable b; - public final CollisionWall wall = new CollisionWall(0, 0, 0, 0, 0, 0, 0, 0, 0); + + public final Vec3 wallWidth = new Vec3(); + public final Vec3 wallHeight = new Vec3(); /** * Time offset from the start of the tick. @@ -350,12 +348,15 @@ public class Collider { */ public float time; - public Collision set(Collideable a, Collideable b, CollisionWall wall, float time) { + public Collision set( + Collideable a, Collideable b, + Wall wall, + float time + ) { this.a = a; this.b = b; - this.wall.getOrigin().set(wall.getOrigin()); - this.wall.getWidth().set(wall.getWidth()); - this.wall.getHeight().set(wall.getHeight()); + wall.getWidth(wallWidth); + wall.getHeight(wallHeight); this.time = time; return this; diff --git a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java index 489bf13..7cc3623 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java +++ b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java @@ -30,6 +30,19 @@ import glm.vec._4.i.Vec4i; */ public class Vectors { + public static final Vec2 ZERO_2 = new Vec2 (0, 0); + public static final Vec2i ZERO_2i = new Vec2i(0, 0); + public static final Vec3 ZERO_3 = new Vec3 (0, 0, 0); + public static final Vec3i ZERO_3i = new Vec3i(0, 0, 0); + public static final Vec4 ZERO_4 = new Vec4 (0, 0, 0, 0); + public static final Vec4i ZERO_4i = new Vec4i(0, 0, 0, 0); + public static final Vec2 UNIT_2 = new Vec2 (1, 1); + public static final Vec2i UNIT_2i = new Vec2i(1, 1); + public static final Vec3 UNIT_3 = new Vec3 (1, 1, 1); + public static final Vec3i UNIT_3i = new Vec3i(1, 1, 1); + public static final Vec4 UNIT_4 = new Vec4 (1, 1, 1, 1); + public static final Vec4i UNIT_4i = new Vec4i(1, 1, 1, 1); + private static final LowOverheadCache VEC3IS = new LowOverheadCache<>(Vec3i::new); diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 6440709..b403ac5 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -42,6 +42,8 @@ public class WorldData { private final Collection entities = Collections.unmodifiableCollection(entitiesById.valueCollection()); + private float time = 0; + public WorldData() { final int size = 1; @@ -96,4 +98,16 @@ public class WorldData { return entities; } + public float getTime() { + return time; + } + + public void advanceTime(float change) { + this.time += change; + } + +// public CollisionModel getCollisionModelOfBlock(Vec3i blockInWorld) { +// Vec3i +// } + } diff --git a/src/main/java/ru/windcorp/progressia/test/AABBRenderer.java b/src/main/java/ru/windcorp/progressia/test/AABBRenderer.java deleted file mode 100644 index e9a6b7b..0000000 --- a/src/main/java/ru/windcorp/progressia/test/AABBRenderer.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.windcorp.progressia.test; - -import ru.windcorp.progressia.client.graphics.model.Shape; -import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; -import ru.windcorp.progressia.client.graphics.model.Shapes; -import ru.windcorp.progressia.client.graphics.texture.Texture; -import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; -import ru.windcorp.progressia.common.collision.AABB; -import ru.windcorp.progressia.common.collision.CollisionModel; -import ru.windcorp.progressia.common.collision.CompoundCollisionModel; - -public class AABBRenderer { - - private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create(); - - public static void renderAABB(AABB aabb, ShapeRenderHelper helper) { - helper.pushTransform().translate(aabb.getOrigin()).scale(aabb.getSize()); - CUBE.render(helper); - helper.popTransform(); - } - - public static void renderAABBsInCompound( - CompoundCollisionModel model, - ShapeRenderHelper helper - ) { - for (CollisionModel part : model.getModels()) { - if (part instanceof CompoundCollisionModel) { - renderAABBsInCompound((CompoundCollisionModel) part, helper); - } else if (part instanceof AABB) { - renderAABB((AABB) part, helper); - } - } - } - -} diff --git a/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java new file mode 100644 index 0000000..15463d4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java @@ -0,0 +1,61 @@ +package ru.windcorp.progressia.test; + +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.client.graphics.model.Shapes; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.common.collision.AABBoid; +import ru.windcorp.progressia.common.collision.CollisionModel; +import ru.windcorp.progressia.common.collision.CompoundCollisionModel; +import ru.windcorp.progressia.common.util.Vectors; + +public class CollisionModelRenderer { + + private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create(); + private static final Shape CUBE_GRAY = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(0.5f, 0.5f, 0.5f).create(); + + public static void renderCollisionModel(CollisionModel model, ShapeRenderHelper helper) { + if (model instanceof AABBoid) { + renderAABBoid((AABBoid) model, helper); + } else if (model instanceof CompoundCollisionModel) { + renderCompound((CompoundCollisionModel) model, helper); + } else { + // Ignore silently + } + } + + private static void renderAABBoid(AABBoid aabb, ShapeRenderHelper helper) { + Mat4 mat = helper.pushTransform(); + Vec3 tmp = Vectors.grab3(); + + aabb.getOrigin(tmp); + mat.translate(tmp); + aabb.getSize(tmp); + mat.scale(tmp); + + Vectors.release(tmp); + + CUBE.render(helper); + helper.popTransform(); + } + + private static void renderCompound( + CompoundCollisionModel model, + ShapeRenderHelper helper + ) { + for (CollisionModel part : model.getModels()) { + renderCollisionModel(part, helper); + } + } + + public static void renderBlock(Vec3i coords, ShapeRenderHelper helper) { + helper.pushTransform().translate(coords.x, coords.y, coords.z); + CUBE_GRAY.render(helper); + helper.popTransform(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java index a58ed35..4c14053 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java @@ -2,6 +2,7 @@ package ru.windcorp.progressia.test; import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.CompoundCollisionModel; +import ru.windcorp.progressia.common.collision.TranslatedAABB; import ru.windcorp.progressia.common.state.IntStateField; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -14,7 +15,7 @@ public class TestEntityDataStatie extends EntityData { super("Test", "Statie"); setCollisionModel(new CompoundCollisionModel( new AABB(0, 0, 0, 1, 1, 1 ), - new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f) + new TranslatedAABB(new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f), 0, 0, 1) )); setSizeNow(16); } From 9c894215f8f88b8e2114047a48cbb48204f2ce9d Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 13 Nov 2020 21:45:05 +0300 Subject: [PATCH 08/24] Added collisions with world --- .../client/graphics/world/LayerWorld.java | 4 +- .../progressia/common/collision/AABB.java | 2 + .../collision/CollisionPathComputer.java | 79 ++++++++++++++++ .../collision/CompoundCollisionModel.java | 6 +- .../collision/WorldCollisionHelper.java | 94 +++++++++++++++++++ .../common/collision/colliders/Collider.java | 63 +++++++++---- .../progressia/common/world/WorldData.java | 17 +++- .../common/world/block/BlockData.java | 6 ++ .../windcorp/progressia/test/TestContent.java | 8 +- .../progressia/test/TestEntityDataStatie.java | 7 +- 10 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/collision/WorldCollisionHelper.java diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 5f74963..d3ce0f4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -124,7 +124,7 @@ public class LayerWorld extends Layer { private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace(); private final List tmp_collideableList = new ArrayList<>(); - private static final boolean RENDER_COLLISION_MODELS = true; + private static final boolean RENDER_COLLISION_MODELS = false; private void tmp_doEveryFrame() { try { @@ -136,7 +136,7 @@ public class LayerWorld extends Layer { } } catch (Throwable e) { e.printStackTrace(); - System.out.println("OLEGSHA is to blame. Tell him he vry stupiDD!!"); + System.err.println("OLEGSHA is to blame. Tell him he vry stupiDD!!"); System.exit(31337); } } diff --git a/src/main/java/ru/windcorp/progressia/common/collision/AABB.java b/src/main/java/ru/windcorp/progressia/common/collision/AABB.java index 9b0fd31..e141673 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/AABB.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/AABB.java @@ -42,6 +42,8 @@ public class AABB implements AABBoid { } + public static final AABB UNIT_CUBE = new AABB(0, 0, 0, 1, 1, 1); + private final Wall[] walls = new Wall[] { new AABBWallImpl(-0.5f, -0.5f, +0.5f, +1, 0, 0, 0, +1, 0), // Top new AABBWallImpl(-0.5f, -0.5f, -0.5f, 0, +1, 0, +1, 0, 0), // Bottom diff --git a/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java b/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java new file mode 100644 index 0000000..eda8e47 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java @@ -0,0 +1,79 @@ +package ru.windcorp.progressia.common.collision; + +import java.util.function.Consumer; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; + +import static java.lang.Math.*; + +public class CollisionPathComputer { + + public static void forEveryBlockInCollisionPath( + Collideable coll, + float maxTime, + Consumer action + ) { + Vec3 displacement = Vectors.grab3(); + coll.getCollideableVelocity(displacement); + displacement.mul(maxTime); + + handleModel(coll.getCollisionModel(), displacement, action); + + Vectors.release(displacement); + } + + private static void handleModel( + CollisionModel model, + Vec3 displacement, + Consumer action + ) { + if (model instanceof CompoundCollisionModel) { + for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) { + handleModel(subModel, displacement, action); + } + } else if (model instanceof AABBoid) { + handleAABBoid((AABBoid) model, displacement, action); + } else { + throw new RuntimeException("not supported"); + } + } + + private static void handleAABBoid(AABBoid model, Vec3 displacement, Consumer action) { + Vec3 size = Vectors.grab3(); + Vec3 origin = Vectors.grab3(); + + model.getOrigin(origin); + model.getSize(size); + + origin.mul(2).sub(size).div(2); // Subtract 0.5*size + + Vec3i pos = Vectors.grab3i(); + + for ( + pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x)); + pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x)); + pos.x += 1 + ) { + for ( + pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y)); + pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y)); + pos.y += 1 + ) { + for ( + pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z)); + pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z)); + pos.z += 1 + ) { + action.accept(pos); + } + } + } + + Vectors.release(origin); + Vectors.release(size); + Vectors.release(pos); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/CompoundCollisionModel.java b/src/main/java/ru/windcorp/progressia/common/collision/CompoundCollisionModel.java index 2cf4572..ab5e766 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/CompoundCollisionModel.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/CompoundCollisionModel.java @@ -8,9 +8,9 @@ import glm.vec._3.Vec3; public class CompoundCollisionModel implements CollisionModel { - private final Collection models; + private final Collection models; - public CompoundCollisionModel(Collection models) { + public CompoundCollisionModel(Collection models) { this.models = models; } @@ -18,7 +18,7 @@ public class CompoundCollisionModel implements CollisionModel { this(ImmutableList.copyOf(models)); } - public Collection getModels() { + public Collection getModels() { return models; } diff --git a/src/main/java/ru/windcorp/progressia/common/collision/WorldCollisionHelper.java b/src/main/java/ru/windcorp/progressia/common/collision/WorldCollisionHelper.java new file mode 100644 index 0000000..ebf2c0b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/collision/WorldCollisionHelper.java @@ -0,0 +1,94 @@ +package ru.windcorp.progressia.common.collision; + +import java.util.ArrayList; +import java.util.Collection; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.LowOverheadCache; +import ru.windcorp.progressia.common.world.WorldData; + +public class WorldCollisionHelper { + + private final Collideable collideable = new Collideable() { + @Override + public boolean onCollision(Collideable other) { + return false; + } + + @Override + public void moveAsCollideable(Vec3 displacement) { + // Ignore + assert displacement.length() < 1e-3f; + } + + @Override + public CollisionModel getCollisionModel() { + return WorldCollisionHelper.this.model; + } + + @Override + public float getCollisionMass() { + return Float.POSITIVE_INFINITY; + } + + @Override + public void getCollideableVelocity(Vec3 output) { + output.set(0); + } + + @Override + public void changeVelocityOnCollision(Vec3 velocityChange) { + // Ignore + assert velocityChange.length() < 1e-3f; + } + }; + + private final Collection activeBlockModels = new ArrayList<>(); + private final CollisionModel model = new CompoundCollisionModel(activeBlockModels); + private final LowOverheadCache blockModelCache = new LowOverheadCache<>(TranslatedAABB::new); + + /** + * Changes the state of this helper's {@link #getCollideable()} so it is ready to adequately handle + * collisions with the {@code collideable} that might happen in the next {@code maxTime} seconds. + * This helper is only valid for checking collisions with the given Collideable and only within + * the given time limit. + * @param collideable the {@link Collideable} that collisions will be checked against + * @param maxTime maximum collision time + */ + public void tuneToCollideable(WorldData world, Collideable collideable, float maxTime) { + activeBlockModels.forEach(blockModelCache::release); + activeBlockModels.clear(); + CollisionPathComputer.forEveryBlockInCollisionPath( + collideable, + maxTime, + v -> addModel(world.getCollisionModelOfBlock(v), v) + ); + } + + private void addModel(CollisionModel model, Vec3i pos) { + if (model == null) { + // Ignore + } else if (model instanceof AABBoid) { + addAABBoidModel((AABBoid) model, pos); + } else if (model instanceof CompoundCollisionModel) { + for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) { + addModel(subModel, pos); + } + } else { + throw new RuntimeException("not supported"); + } + } + + private void addAABBoidModel(AABBoid model, Vec3i pos) { + TranslatedAABB translator = blockModelCache.grab(); + translator.setParent(model); + translator.setTranslation(pos.x, pos.y, pos.z); + activeBlockModels.add(translator); + } + + public Collideable getCollideable() { + return collideable; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java index 2e6b591..8fdbc90 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java @@ -33,7 +33,7 @@ public class Collider { return; } - Collision firstCollision = getFirstCollision(colls, tickLength, workspace); + Collision firstCollision = getFirstCollision(colls, tickLength, world, workspace); if (firstCollision == null) { break; @@ -52,39 +52,43 @@ public class Collider { private static Collision getFirstCollision( List colls, float tickLength, + WorldData world, ColliderWorkspace workspace ) { Collision result = null; + Collideable worldColl = workspace.worldCollisionHelper.getCollideable(); // For every pair of colls for (int i = 0; i < colls.size(); ++i) { Collideable a = colls.get(i); + tuneWorldCollisionHelper(a, tickLength, world, workspace); + + result = workspace.updateLatestCollision( + result, + getCollision(a, worldColl, tickLength, workspace) + ); + for (int j = i + 1; j < colls.size(); ++j) { Collideable b = colls.get(j); - Collision collision = getCollision(a, b, tickLength, workspace); - - // Update result - if (collision != null) { - Collision second; - - if (result == null || collision.time < result.time) { - second = result; - result = collision; - } else { - second = collision; - } - - // Release Collision that is no longer used - if (second != null) workspace.release(second); - } + result = workspace.updateLatestCollision(result, collision); } } return result; } + private static void tuneWorldCollisionHelper( + Collideable coll, + float tickLength, + WorldData world, + ColliderWorkspace workspace + ) { + WorldCollisionHelper wch = workspace.worldCollisionHelper; + wch.tuneToCollideable(world, coll, tickLength); + } + static Collision getCollision( Collideable a, Collideable b, @@ -104,7 +108,7 @@ public class Collider { float tickLength, ColliderWorkspace workspace ) { - if (aModel instanceof AABBoid && bModel instanceof AABBoid) { /*replace AABB with AABBoid where makes sense, also add TranslatedAABB support in TestAABBRenderer*/ + if (aModel instanceof AABBoid && bModel instanceof AABBoid) { return AABBoidCollider.computeModelCollision( aBody, bBody, (AABBoid) aModel, (AABBoid) bModel, @@ -324,6 +328,8 @@ public class Collider { new LowOverheadCache<>(Collision::new); AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1); + + WorldCollisionHelper worldCollisionHelper = new WorldCollisionHelper(); Collision grab() { return collisionCache.grab(); @@ -333,6 +339,27 @@ public class Collider { collisionCache.release(object); } + Collision updateLatestCollision(Collision a, Collision b) { + if (a == null) { + return b; // may be null + } else if (b == null) { + return a; + } + + Collision first, second; + + if (a.time > b.time) { + first = b; + second = a; + } else { + first = a; + second = b; + } + + release(second); + return first; + } + } static class Collision { diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index b403ac5..6584884 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -24,8 +24,10 @@ import glm.vec._3.i.Vec3i; import gnu.trove.impl.sync.TSynchronizedLongObjectMap; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; +import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; public class WorldData { @@ -106,8 +108,17 @@ public class WorldData { this.time += change; } -// public CollisionModel getCollisionModelOfBlock(Vec3i blockInWorld) { -// Vec3i -// } + public CollisionModel getCollisionModelOfBlock(Vec3i blockInWorld) { + ChunkData chunk = getChunkByBlock(blockInWorld); + if (chunk == null) return null; + + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk); + BlockData block = chunk.getBlock(blockInChunk); + Vectors.release(blockInChunk); + + if (block == null) return null; + return block.getCollisionModel(); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java index 5bcbd50..e1ea62d 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java @@ -17,6 +17,8 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.block; +import ru.windcorp.progressia.common.collision.AABB; +import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.Namespaced; public class BlockData extends Namespaced { @@ -24,5 +26,9 @@ public class BlockData extends Namespaced { public BlockData(String namespace, String name) { super(namespace, name); } + + public CollisionModel getCollisionModel() { + return AABB.UNIT_CUBE; + } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 3c890bc..988e4f8 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -14,6 +14,7 @@ import ru.windcorp.progressia.client.world.block.*; import ru.windcorp.progressia.client.world.entity.*; import ru.windcorp.progressia.client.world.tile.*; import ru.windcorp.progressia.common.collision.AABB; +import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; import ru.windcorp.progressia.common.world.ChunkData; @@ -41,7 +42,12 @@ public class TestContent { } private static void registerBlocks() { - register(new BlockData("Test", "Air")); + register(new BlockData("Test", "Air") { + @Override + public CollisionModel getCollisionModel() { + return null; + } + }); register(new BlockRenderNone("Test", "Air")); register(new BlockLogic("Test", "Air")); diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java index 4c14053..19d47c9 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java @@ -1,8 +1,6 @@ package ru.windcorp.progressia.test; import ru.windcorp.progressia.common.collision.AABB; -import ru.windcorp.progressia.common.collision.CompoundCollisionModel; -import ru.windcorp.progressia.common.collision.TranslatedAABB; import ru.windcorp.progressia.common.state.IntStateField; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -13,10 +11,7 @@ public class TestEntityDataStatie extends EntityData { public TestEntityDataStatie() { super("Test", "Statie"); - setCollisionModel(new CompoundCollisionModel( - new AABB(0, 0, 0, 1, 1, 1 ), - new TranslatedAABB(new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f), 0, 0, 1) - )); + setCollisionModel(new AABB(0, 0, 0, 1, 1, 1)); setSizeNow(16); } From 154cc0a3b830d522ef99932641325c3dab0d57a1 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 14 Nov 2020 13:49:35 +0300 Subject: [PATCH 09/24] Added gravity and refactored player controls - Added gravity (still testing) - Two modes: realistic (9.8 m/s^2) and Minecraft (32 m/s^2) - Switch with G - Changing (0; 0; 0) rebound to H - Added walking controls - Minecraft-style - WIP - LayerTestGUI now displays dev options - Added Units --- .../progressia/client/ClientState.java | 2 +- .../client/graphics/gui/LayerTestGUI.java | 125 -------- .../client/graphics/world/Camera.java | 4 + .../client/graphics/world/LayerWorld.java | 169 ++--------- .../ru/windcorp/progressia/common/Units.java | 56 ++++ .../progressia/test/LayerTestGUI.java | 155 ++++++++++ .../windcorp/progressia/test/TestContent.java | 2 +- .../progressia/test/TestPlayerControls.java | 270 ++++++++++++++++++ 8 files changed, 516 insertions(+), 267 deletions(-) delete mode 100755 src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java create mode 100644 src/main/java/ru/windcorp/progressia/common/Units.java create mode 100755 src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java create mode 100644 src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index 84c26a2..b812907 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -3,10 +3,10 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; -import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI; import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.server.ServerState; +import ru.windcorp.progressia.test.LayerTestGUI; public class ClientState { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java deleted file mode 100755 index 6a74d96..0000000 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************* - * Progressia - * Copyright (C) 2020 Wind Corporation - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - *******************************************************************************/ -package ru.windcorp.progressia.client.graphics.gui; - -import com.google.common.eventbus.Subscribe; -import glm.vec._2.i.Vec2i; -import org.lwjgl.glfw.GLFW; -import ru.windcorp.progressia.client.graphics.Colors; -import ru.windcorp.progressia.client.graphics.flat.RenderTarget; -import ru.windcorp.progressia.client.graphics.font.Font; -import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent; -import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; -import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; -import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.client.localization.Localizer; -import ru.windcorp.progressia.client.localization.MutableString; -import ru.windcorp.progressia.client.localization.MutableStringLocalized; - -public class LayerTestGUI extends GUILayer { - - private static class DebugComponent extends Component { - private final int color; - - public DebugComponent(String name, Vec2i size, int color) { - super(name); - this.color = color; - - setPreferredSize(size); - - addListener(new Object() { - @Subscribe - public void onHoverChanged(HoverEvent e) { - requestReassembly(); - } - }); - - addListener(KeyEvent.class, this::onClicked); - } - - private boolean onClicked(KeyEvent event) { - if (!event.isMouse()) { - return false; - } else if (event.isPress() && event.isLeftMouseButton()) { - System.out.println("You pressed a Component!"); - } - return true; - } - - @Override - protected void assembleSelf(RenderTarget target) { - target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK); - - target.fill( - getX() + 2, getY() + 2, - getWidth() - 4, getHeight() - 4, - isHovered() ? Colors.DEBUG_YELLOW : color - ); - } - } - - public LayerTestGUI() { - super("LayerTestGui", new LayoutAlign(1, 0.75, 5)); - - Panel panel = new Panel("Alex", new LayoutVertical(5)); - - panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44)); - - Component charlie = new DebugComponent("Charlie", null, 0x222222); - charlie.setLayout(new LayoutVertical(5)); - - //Debug - Localizer.getInstance().setLanguage("ru-RU"); - MutableString epsilon = new MutableStringLocalized("Epsilon") - .addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four"); - // These two are swapped in code due to a bug in layouts, fixing ATM - charlie.addChild( - new Label( - "Delta", - new Font().withColor(0xCCBB44).deriveShadow().deriveBold(), - "Пре-альфа!" - ) - ); - charlie.addChild( - new Label( - "Epsilon", - new Font().withColor(0x4444BB).deriveItalic(), - () -> epsilon.get().concat("\u269b") - ) - ); - panel.addChild(charlie); - - - charlie.addListener(KeyEvent.class, e -> { - if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) { - Localizer localizer = Localizer.getInstance(); - if (localizer.getLanguage().equals("ru-RU")) { - localizer.setLanguage("en-US"); - } else { - localizer.setLanguage("ru-RU"); - } - return true; - } return false; - }); - charlie.setFocusable(true); - charlie.takeFocus(); - - getRoot().addChild(panel); - } - -} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java index ab99777..56748f9 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java @@ -271,6 +271,10 @@ public class Camera { currentModeIndex++; } } + + public int getCurrentModeIndex() { + return currentModeIndex; + } public float getLastAnchorYaw() { return lastAnchorYaw; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index d3ce0f4..fb7557e 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -20,41 +20,27 @@ package ru.windcorp.progressia.client.graphics.world; import java.util.ArrayList; import java.util.List; -import org.lwjgl.glfw.GLFW; - -import glm.Glm; -import glm.mat._3.Mat3; -import glm.vec._2.Vec2; -import glm.vec._3.Vec3; import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.comms.controls.InputBasedControls; import ru.windcorp.progressia.client.graphics.Layer; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; -import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; -import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; -import ru.windcorp.progressia.client.graphics.input.InputEvent; -import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.colliders.Collider; -import ru.windcorp.progressia.common.util.FloatMathUtils; -import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.test.CollisionModelRenderer; +import ru.windcorp.progressia.test.TestPlayerControls; public class LayerWorld extends Layer { - private final Mat3 angMat = new Mat3(); - - private int movementForward = 0; - private int movementRight = 0; - private int movementUp = 0; - private final WorldRenderHelper helper = new WorldRenderHelper(); private final Client client; private final InputBasedControls inputBasedControls; + private final TestPlayerControls tmp_testControls = TestPlayerControls.getInstance(); public LayerWorld(Client client) { super("World"); @@ -75,40 +61,12 @@ public class LayerWorld extends Layer { @Override protected void doRender() { - if (client.getLocalPlayer() != null) { - tmp_handleControls(); - } - Camera camera = client.getCamera(); if (camera.hasAnchor()) { renderWorld(); } } - private void tmp_handleControls() { - EntityData player = client.getLocalPlayer(); - - angMat.identity().rotateZ(player.getYaw()); - - Vec3 movement = Vectors.grab3(); - - // Horizontal and vertical max control speed - final float movementSpeed = 0.1f * 60.0f; - // (0; 1], 1 is instant change, 0 is no control authority - final float controlAuthority = 0.1f; - - movement.set(movementForward, -movementRight, 0); - if (movementForward != 0 && movementRight != 0) movement.normalize(); - angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped - movement.z = movementUp; - movement.mul(movementSpeed); - movement.sub(player.getVelocity()); - movement.mul(controlAuthority); - player.getVelocity().add(movement); - - Vectors.release(movement); - } - private void renderWorld() { client.getCamera().apply(helper); FaceCulling.push(true); @@ -127,11 +85,16 @@ public class LayerWorld extends Layer { private static final boolean RENDER_COLLISION_MODELS = false; private void tmp_doEveryFrame() { + float tickLength = (float) GraphicsInterface.getFrameLength(); + try { - tmp_performCollisions(); + tmp_performCollisions(tickLength); + + tmp_testControls.applyPlayerControls(); for (EntityData data : this.client.getWorld().getData().getEntities()) { tmp_applyFriction(data); + tmp_applyGravity(data, tickLength); tmp_renderCollisionModel(data); } } catch (Throwable e) { @@ -147,121 +110,47 @@ public class LayerWorld extends Layer { } } - private void tmp_performCollisions() { + private void tmp_performCollisions(float tickLength) { tmp_collideableList.clear(); tmp_collideableList.addAll(this.client.getWorld().getData().getEntities()); Collider.performCollisions( tmp_collideableList, this.client.getWorld().getData(), - (float) GraphicsInterface.getFrameLength(), + tickLength, tmp_colliderWorkspace ); } private void tmp_applyFriction(EntityData entity) { - final float frictionCoeff = 1 - 1e-2f; + final float frictionCoeff = 1 - 1e-5f; entity.getVelocity().mul(frictionCoeff); } + + private void tmp_applyGravity(EntityData entity, float tickLength) { + if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) { + return; + } + + final float gravitationalAcceleration; + + if (tmp_testControls.useMinecraftGravity()) { + gravitationalAcceleration = 32f * Units.METERS_PER_SECOND_SQUARED; // plz dont sue me M$ + } else { + gravitationalAcceleration = 9.81f * Units.METERS_PER_SECOND_SQUARED; + } + entity.getVelocity().add(0, 0, -gravitationalAcceleration * tickLength); + } @Override protected void handleInput(Input input) { if (input.isConsumed()) return; - InputEvent event = input.getEvent(); - - if (event instanceof KeyEvent) { - if (onKeyEvent((KeyEvent) event)) { - input.consume(); - } - } else if (event instanceof CursorMoveEvent) { - onMouseMoved((CursorMoveEvent) event); - input.consume(); - } + tmp_testControls.handleInput(input); if (!input.isConsumed()) { inputBasedControls.handleInput(input); } } - - private boolean flag = true; - - private boolean onKeyEvent(KeyEvent event) { - if (event.isRepeat()) return false; - - int multiplier = event.isPress() ? 1 : -1; - - switch (event.getKey()) { - case GLFW.GLFW_KEY_W: - movementForward += +1 * multiplier; - break; - case GLFW.GLFW_KEY_S: - movementForward += -1 * multiplier; - break; - case GLFW.GLFW_KEY_A: - movementRight += -1 * multiplier; - break; - case GLFW.GLFW_KEY_D: - movementRight += +1 * multiplier; - break; - case GLFW.GLFW_KEY_SPACE: - movementUp += +1 * multiplier; - break; - case GLFW.GLFW_KEY_LEFT_SHIFT: - movementUp += -1 * multiplier; - break; - - case GLFW.GLFW_KEY_ESCAPE: - if (!event.isPress()) return false; - - if (flag) { - GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); - } else { - GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); - } - - flag = !flag; - break; - - case GLFW.GLFW_KEY_F5: - if (!event.isPress()) return false; - - if (client.getCamera().hasAnchor()) { - client.getCamera().selectNextMode(); - } - break; - - default: - return false; - } - - return true; - } - - private void onMouseMoved(CursorMoveEvent event) { - if (!flag) return; - - final float yawScale = -0.002f; - final float pitchScale = yawScale; - - EntityData player = client.getLocalPlayer(); - - if (player != null) { - normalizeAngles(player.getDirection().add( - (float) (event.getChangeX() * yawScale), - (float) (event.getChangeY() * pitchScale) - )); - } - } - - private void normalizeAngles(Vec2 dir) { - // Normalize yaw - dir.x = FloatMathUtils.normalizeAngle(dir.x); - - // Clamp pitch - dir.y = Glm.clamp( - dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 - ); - } } diff --git a/src/main/java/ru/windcorp/progressia/common/Units.java b/src/main/java/ru/windcorp/progressia/common/Units.java new file mode 100644 index 0000000..0c8348a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/Units.java @@ -0,0 +1,56 @@ +package ru.windcorp.progressia.common; + +public class Units { + + // Base units + // We're SI. + public static final float METERS = 1; + public static final float KILOGRAMS = 1; + public static final float SECONDS = 1; + + // Length + public static final float CENTIMETERS = METERS / 100; + public static final float MILLIMETERS = METERS / 1000; + public static final float KILOMETERS = METERS * 1000; + + // Surface + public static final float SQUARE_CENTIMETERS = CENTIMETERS * CENTIMETERS; + public static final float SQUARE_METERS = METERS * METERS; + public static final float SQUARE_MILLIMETERS = MILLIMETERS * MILLIMETERS; + public static final float SQUARE_KILOMETERS = KILOMETERS * KILOMETERS; + + // Volume + public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS; + public static final float CUBIC_METERS = METERS * METERS * METERS; + public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS; + public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS; + + // Mass + public static final float GRAMS = KILOGRAMS / 1000; + public static final float TONNES = KILOGRAMS * 1000; + + // Density + public static final float KILOGRAMS_PER_CUBIC_METER = KILOGRAMS / CUBIC_METERS; + public static final float GRAMS_PER_CUBIC_CENTIMETER = GRAMS / CUBIC_CENTIMETERS; + + // Time + public static final float MILLISECONDS = SECONDS / 1000; + public static final float MINUTES = SECONDS * 60; + public static final float HOURS = MINUTES * 60; + public static final float DAYS = HOURS * 24; + + // Frequency + public static final float HERTZ = 1 / SECONDS; + public static final float KILOHERTZ = HERTZ * 1000; + + // Velocity + public static final float METERS_PER_SECOND = METERS / SECONDS; + public static final float KILOMETERS_PER_HOUR = KILOMETERS / HOURS; + + // Acceleration + public static final float METERS_PER_SECOND_SQUARED = METERS_PER_SECOND / SECONDS; + + // Force + public static final float NEWTONS = METERS_PER_SECOND_SQUARED * KILOGRAMS; + +} diff --git a/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java new file mode 100755 index 0000000..2ae9828 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Progressia + * Copyright (C) 2020 Wind Corporation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *******************************************************************************/ +package ru.windcorp.progressia.test; + +import java.util.ArrayList; +import java.util.Collection; + +import ru.windcorp.progressia.client.ClientState; +import ru.windcorp.progressia.client.graphics.font.Font; +import ru.windcorp.progressia.client.graphics.gui.GUILayer; +import ru.windcorp.progressia.client.graphics.gui.Label; +import ru.windcorp.progressia.client.graphics.gui.Panel; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; + +public class LayerTestGUI extends GUILayer { + + public LayerTestGUI() { + super("LayerTestGui", new LayoutAlign(0, 1, 5)); + + Panel panel = new Panel("ControlDisplays", new LayoutVertical(5)); + + Collection

- * TODO document - * @param throwable - * @param messageFormat - * @param args - */ - public static void crash(Throwable throwable, String messageFormat, Object... args) { - StringBuilder output = new StringBuilder(); - - appendContextProviders(output); - addSeparator(output); - if (appendAnalyzers(output, throwable, messageFormat, args)) { - addSeparator(output); - } - - appendMessageFormat(output, messageFormat, args); - - appendStackTrace(output, throwable); - - export(output.toString()); - - System.exit(0); - } - - private static void appendContextProviders(StringBuilder output) { - - // Do a local copy to avoid deadlocks -OLEGSHA - ContextProvider[] localProvidersCopy = - PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); - - for (ContextProvider provider : localProvidersCopy) { - if (provider == null) continue; - - addSeparator(output); - - try { - Map buf = new HashMap<>(); - provider.provideContext(buf); - - if (!buf.isEmpty()) { - output.append("Provider name: ").append(provider.getName()).append("\n"); - for (Map.Entry entry : buf.entrySet()) { - output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); - } - } - } catch (Throwable t) { - output.append("\n"); - - String providerName; - - try { - providerName = provider.getName(); - } catch (Throwable t1) { - providerName = provider.getClass().getName(); - } - - output.append(providerName).append(" is broken").append("\n"); - // ContextProvider is broken - } - } - } - - private static boolean appendAnalyzers( - StringBuilder output, - Throwable throwable, String messageFormat, Object[] args - ) { - boolean analyzerResponsesExist = false; - - // Do a local copy to avoid deadlocks -OLEGSHA - Analyzer[] localAnalyzersCopy = - ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); - - for (Analyzer analyzer : localAnalyzersCopy) { - if (analyzer == null) continue; - - String answer; - try { - answer = analyzer.analyze(throwable, messageFormat, args); - - if (answer != null && !answer.isEmpty()) { - analyzerResponsesExist = true; - output.append(analyzer.getName()).append(": ").append(answer).append("\n"); - } - } catch (Throwable t) { - analyzerResponsesExist = true; - - output.append("\n"); - - String analyzerName; - - try { - analyzerName = analyzer.getName(); - } catch (Throwable t1) { - analyzerName = analyzer.getClass().getName(); - } - - output.append(analyzerName).append(" is broken").append("\n"); - // Analyzer is broken - } - } - - return analyzerResponsesExist; - } - - private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { - output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n"); - - addSeparator(output); - } - - private static void appendStackTrace(StringBuilder output, Throwable throwable) { - output.append("Stacktrace: \n"); - - if (throwable == null) { - output.append("no Throwable provided").append("\n"); - return; - } - - // Formatting to a human-readable string - Writer sink = new StringBuilderWriter(output); - try { - throwable.printStackTrace(new PrintWriter(sink)); - } catch (Exception e) { - // PLAK - } - output.append("\n"); - } - - private static void export(String report) { - try { - LOGGER.fatal("/n" + report); - } catch (Exception e) { - // PLAK - } - - System.err.println(report); - - generateCrashReportFiles(report); - } - - private static void generateCrashReportFiles(String output) { - Date date = new Date(); - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); - - try { - if (!Files.exists(CRASH_REPORTS_PATH)) Files.createDirectory(CRASH_REPORTS_PATH); - - createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); - createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); - } catch (Throwable t) { - // Crash Report not created - } - } - - private static void createFileForCrashReport(String buffer, String filename) { - try ( - BufferedWriter writer = Files.newBufferedWriter( - Paths.get(filename), - StandardCharsets.UTF_8 - ) - ) { - writer.write(buffer); - } catch (IOException ex) { - // Crash Report not created - } - } - - public static void registerProvider(ContextProvider provider) { - PROVIDERS.add(provider); - } - - public static void registerAnalyzer(Analyzer analyzer) { - ANALYZERS.add(analyzer); - } - - private static void addSeparator(StringBuilder sb) { - sb.append( - // 80 chars - "--------------------------------------------------------------------------------" - ).append("\n"); - } -} +package ru.windcorp.progressia.common.util.crash; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.util.StringBuilderWriter; + +import ru.windcorp.jputil.chars.StringUtil; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +public class CrashReports { + + private CrashReports() { + } + + private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); + + private static final Collection PROVIDERS = Collections.synchronizedCollection(new ArrayList<>()); + + private static final Collection ANALYZERS = Collections.synchronizedCollection(new ArrayList<>()); + + private static final Logger LOGGER = LogManager.getLogger("crash"); + + /** + * This method never returns. + *

+ * TODO document + * + * @param throwable + * @param messageFormat + * @param args + */ + public static void report(Throwable throwable, String messageFormat, Object... args) { + StringBuilder output = new StringBuilder(); + + appendContextProviders(output); + addSeparator(output); + if (appendAnalyzers(output, throwable, messageFormat, args)) { + addSeparator(output); + } + + appendMessageFormat(output, messageFormat, args); + + appendStackTrace(output, throwable); + + export(output.toString()); + + System.exit(0); + } + + private static void appendContextProviders(StringBuilder output) { + + // Do a local copy to avoid deadlocks -OLEGSHA + ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); + + for (ContextProvider provider : localProvidersCopy) { + if (provider == null) + continue; + + addSeparator(output); + + try { + Map buf = new HashMap<>(); + provider.provideContext(buf); + + if (!buf.isEmpty()) { + output.append("Provider name: ").append(provider.getName()).append("\n"); + for (Map.Entry entry : buf.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + } + } catch (Throwable t) { + output.append("\n"); + + String providerName; + + try { + providerName = provider.getName(); + } catch (Throwable t1) { + providerName = provider.getClass().getName(); + } + + output.append(providerName).append(" is broken").append("\n"); + // ContextProvider is broken + } + } + } + + private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat, + Object[] args) { + boolean analyzerResponsesExist = false; + + // Do a local copy to avoid deadlocks -OLEGSHA + Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); + + for (Analyzer analyzer : localAnalyzersCopy) { + if (analyzer == null) + continue; + + String answer; + try { + answer = analyzer.analyze(throwable, messageFormat, args); + + if (answer != null && !answer.isEmpty()) { + analyzerResponsesExist = true; + output.append(analyzer.getName()).append(": ").append(answer).append("\n"); + } + } catch (Throwable t) { + analyzerResponsesExist = true; + + output.append("\n"); + + String analyzerName; + + try { + analyzerName = analyzer.getName(); + } catch (Throwable t1) { + analyzerName = analyzer.getClass().getName(); + } + + output.append(analyzerName).append(" is broken").append("\n"); + // Analyzer is broken + } + } + + return analyzerResponsesExist; + } + + private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { + output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n"); + + addSeparator(output); + } + + private static void appendStackTrace(StringBuilder output, Throwable throwable) { + output.append("Stacktrace: \n"); + + if (throwable == null) { + output.append("no Throwable provided").append("\n"); + return; + } + + // Formatting to a human-readable string + Writer sink = new StringBuilderWriter(output); + try { + throwable.printStackTrace(new PrintWriter(sink)); + } catch (Exception e) { + // PLAK + } + output.append("\n"); + } + + private static void export(String report) { + try { + LOGGER.fatal("/n" + report); + } catch (Exception e) { + // PLAK + } + + System.err.println(report); + + generateCrashReportFiles(report); + } + + private static void generateCrashReportFiles(String output) { + Date date = new Date(); + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); + + try { + if (!Files.exists(CRASH_REPORTS_PATH)) + Files.createDirectory(CRASH_REPORTS_PATH); + + createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); + createFileForCrashReport(output, + CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); + } catch (Throwable t) { + // Crash Report not created + } + } + + private static void createFileForCrashReport(String buffer, String filename) { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) { + writer.write(buffer); + } catch (IOException ex) { + // Crash Report not created + } + } + + public static void registerProvider(ContextProvider provider) { + PROVIDERS.add(provider); + } + + public static void registerAnalyzer(Analyzer analyzer) { + ANALYZERS.add(analyzer); + } + + private static void addSeparator(StringBuilder sb) { + sb.append( + // 80 chars + "--------------------------------------------------------------------------------").append("\n"); + } +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java index f6c5c8b..1b6d5bb 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java @@ -7,25 +7,26 @@ import java.io.IOException; import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.DataBuffer; +import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.WorldData; public class PacketEntityChange extends PacketWorldChange { - + private long entityId; private final DataBuffer buffer = new DataBuffer(); public PacketEntityChange() { super("Core", "EntityChange"); } - + public long getEntityId() { return entityId; } - + public void setEntityId(long entityId) { this.entityId = entityId; } - + public DataBuffer getBuffer() { return buffer; } @@ -41,19 +42,15 @@ public class PacketEntityChange extends PacketWorldChange { @Override public void apply(WorldData world) { EntityData entity = world.getEntity(getEntityId()); - + if (entity == null) { - throw new RuntimeException( - "Entity with ID " + getEntityId() + " not found" - ); + CrashReports.report(null, "Entity with ID %d not found", getEntityId()); } - + try { entity.read(getReader(), IOContext.COMMS); } catch (IOException e) { - throw new RuntimeException( - "Entity could not be read", e - ); + CrashReports.report(e, "Entity could not be read"); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java index 70fada1..960f77d 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java @@ -11,6 +11,7 @@ import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.state.IOContext; import ru.windcorp.progressia.common.util.LowOverheadCache; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.block.BlockData; @@ -21,50 +22,50 @@ import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.server.Server; public class ImplementedChangeTracker implements Changer { - + public static interface ChangeImplementation { void applyOnServer(WorldData world); Packet asPacket(); } - + private static class SetBlock extends PacketWorldChange implements ChangeImplementation { private final Vec3i position = new Vec3i(); private BlockData block; - + public SetBlock() { super("Core", "SetBlock"); } - + public void initialize(Vec3i position, BlockData block) { this.position.set(position.x, position.y, position.z); this.block = block; } - + @Override public void applyOnServer(WorldData world) { Vec3i blockInChunk = Vectors.grab3i(); Coordinates.convertInWorldToInChunk(position, blockInChunk); - + world.getChunkByBlock(position).setBlock(blockInChunk, block); - + Vectors.release(blockInChunk); } - + @Override public void apply(WorldData world) { applyOnServer(world); } - + @Override public Packet asPacket() { return this; } - + } - + private static class AddOrRemoveTile extends PacketWorldChange implements ChangeImplementation { @@ -72,13 +73,13 @@ public class ImplementedChangeTracker implements Changer { private final Vec3i position = new Vec3i(); private BlockFace face; private TileData tile; - + private boolean shouldAdd; - + public AddOrRemoveTile() { super("Core", "AddOrRemoveTile"); } - + public void initialize( Vec3i position, BlockFace face, TileData tile, @@ -89,52 +90,51 @@ public class ImplementedChangeTracker implements Changer { this.tile = tile; this.shouldAdd = shouldAdd; } - + @Override public void applyOnServer(WorldData world) { Vec3i blockInChunk = Vectors.grab3i(); Coordinates.convertInWorldToInChunk(position, blockInChunk); - - List tiles = world.getChunkByBlock(position) - .getTiles(blockInChunk, face); - + + List tiles = world.getChunkByBlock(position).getTiles(blockInChunk, face); + if (shouldAdd) { tiles.add(tile); } else { tiles.remove(tile); } - + Vectors.release(blockInChunk); } - + @Override public void apply(WorldData world) { applyOnServer(world); } - + @Override public Packet asPacket() { return this; } - + } - + private static class ChangeEntity implements ChangeImplementation { - + private EntityData entity; private Change change; - + private final PacketEntityChange packet = new PacketEntityChange(); public void set(T entity, Change change) { this.entity = entity; this.change = change; - + packet.setEntityId(entity.getEntityId()); try { entity.write(packet.getWriter(), IOContext.COMMS); } catch (IOException e) { - throw new RuntimeException(e); + CrashReports.report(e, "__DOC__ME__"); } } @@ -142,11 +142,11 @@ public class ImplementedChangeTracker implements Changer { @Override public void applyOnServer(WorldData world) { ((Change) change).change(entity); - + try { entity.write(packet.getWriter(), IOContext.COMMS); } catch (IOException e) { - throw new RuntimeException("Entity could not be written", e); + CrashReports.report(e, "Entity could not be written"); } } @@ -154,11 +154,11 @@ public class ImplementedChangeTracker implements Changer { public Packet asPacket() { return packet; } - + } private final List changes = new ArrayList<>(1024); - + private final LowOverheadCache setBlockCache = new LowOverheadCache<>(SetBlock::new); @@ -174,21 +174,21 @@ public class ImplementedChangeTracker implements Changer { change.initialize(pos, block); changes.add(change); } - + @Override public void addTile(Vec3i block, BlockFace face, TileData tile) { AddOrRemoveTile change = addOrRemoveTileCache.grab(); change.initialize(block, face, tile, true); changes.add(change); } - + @Override public void removeTile(Vec3i block, BlockFace face, TileData tile) { AddOrRemoveTile change = addOrRemoveTileCache.grab(); change.initialize(block, face, tile, false); changes.add(change); } - + @Override public void changeEntity( T entity, Change change @@ -197,7 +197,7 @@ public class ImplementedChangeTracker implements Changer { changeRecord.set(entity, change); changes.add(changeRecord); } - + public void applyChanges(Server server) { changes.forEach(c -> c.applyOnServer(server.getWorld().getData())); changes.stream().map(ChangeImplementation::asPacket).filter(Objects::nonNull).forEach( @@ -206,7 +206,7 @@ public class ImplementedChangeTracker implements Changer { changes.forEach(this::release); changes.clear(); } - + private void release(ChangeImplementation c) { if (c instanceof SetBlock) { setBlockCache.release((SetBlock) c); From 38a8a885c8d1b795a4de68dca823240284626d50 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 14 Nov 2020 21:58:48 +0300 Subject: [PATCH 11/24] Updated build.gradle to make build artifacts easily runnable --- build.gradle | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 1eff0a5..f442d6d 100644 --- a/build.gradle +++ b/build.gradle @@ -18,10 +18,12 @@ dependencies { implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' // log4j - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' + implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' testImplementation 'junit:junit:4.12' + + // See also LWJGL dependencies below } /* @@ -77,3 +79,22 @@ dependencies { } // LWJGL END + +jar { + manifest { + attributes( + "Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain", + "Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() }.join(' ') + ) + } +} + +/* + * Copies runtime dependencies to a prespecified location so they can be packaged properly. + */ +task copyLibs(type: Copy) { + into "${libsDir}/lib" + from configurations.runtimeClasspath +} + +build.dependsOn(copyLibs) From f2e28161a895831406deeb29c47f482e6d6cfcae Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sun, 15 Nov 2020 20:58:17 +0300 Subject: [PATCH 12/24] Added human player model - Extracted NPedModel out of QuadripedModel - Both are now configurable - Added HumanoidModel - Added human model - Changed NPedModel animation logic - Added a few skins including first sketch of Pyotr by WarDreg - Decreased walking speed --- .../client/graphics/model/LambdaModel.java | 9 + .../graphics/texture/ComplexTexture.java | 4 +- .../client/world/entity/HumanoidModel.java | 116 +++++++ .../client/world/entity/NPedModel.java | 284 ++++++++++++++++++ .../client/world/entity/QuadripedModel.java | 200 ++---------- .../progressia/common/world/ChunkData.java | 36 ++- .../windcorp/progressia/test/TestContent.java | 11 +- .../test/TestEntityRenderHuman.java | 164 ++++++++++ .../progressia/test/TestPlayerControls.java | 2 +- .../assets/textures/entities/pyotr.png | Bin 0 -> 8036 bytes .../assets/textures/entities/test_skin.png | Bin 0 -> 16530 bytes .../textures/entities/test_skin_layout.png | Bin 0 -> 2850 bytes 12 files changed, 627 insertions(+), 199 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java create mode 100644 src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java create mode 100644 src/main/resources/assets/textures/entities/pyotr.png create mode 100644 src/main/resources/assets/textures/entities/test_skin.png create mode 100644 src/main/resources/assets/textures/entities/test_skin_layout.png diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java index c6799c5..00d47b1 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/LambdaModel.java @@ -121,5 +121,14 @@ public class LambdaModel extends DynamicModel { } } + + public static LambdaModel animate(Renderable model, TransformGetter transform) { + return new LambdaModel( + new Renderable[] { model }, + new Mat4[] { new Mat4() }, + new boolean[] { true }, + new TransformGetter[] { transform } + ); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java index d8f4c18..8aaf4d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java @@ -19,10 +19,10 @@ public class ComplexTexture { this.primitive = primitive; this.assumedWidth = abstractWidth - * primitive.getWidth() / (float) primitive.getBufferWidth(); + / (float) primitive.getWidth() * primitive.getBufferWidth(); this.assumedHeight = abstractHeight - * primitive.getHeight() / (float) primitive.getBufferHeight(); + / (float) primitive.getHeight() * primitive.getBufferHeight(); } public Texture get(int x, int y, int width, int height) { diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java new file mode 100644 index 0000000..45817db --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/HumanoidModel.java @@ -0,0 +1,116 @@ +package ru.windcorp.progressia.client.world.entity; + +import static java.lang.Math.*; +import static ru.windcorp.progressia.common.util.FloatMathUtils.*; + +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class HumanoidModel extends NPedModel { + + protected static abstract class Limb extends BodyPart { + private final float animationOffset; + + public Limb( + Renderable renderable, Vec3 joint, + float animationOffset + ) { + super(renderable, joint); + this.animationOffset = animationOffset; + } + + @Override + protected void applyTransform(Mat4 mat, NPedModel model) { + float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; + float value = sin(phase); + float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter(); + mat.rotateY(value * amplitude); + } + + protected abstract float getSwingAmplitude(HumanoidModel model); + + } + + public static class Leg extends Limb { + public Leg( + Renderable renderable, Vec3 joint, + float animationOffset + ) { + super(renderable, joint, animationOffset); + } + + @Override + protected float getSwingAmplitude(HumanoidModel model) { + return model.walkingLegSwing; + } + } + + public static class Arm extends Limb { + public Arm( + Renderable renderable, Vec3 joint, + float animationOffset + ) { + super(renderable, joint, animationOffset); + } + + @Override + protected float getSwingAmplitude(HumanoidModel model) { + return model.walkingArmSwing; + } + } + + private final Arm leftArm; + private final Arm rightArm; + private final Leg leftLeg; + private final Leg rightLeg; + + private float walkingLegSwing; + private float walkingArmSwing; + + public HumanoidModel( + EntityData entity, + + Body body, Head head, + Arm leftArm, Arm rightArm, + Leg leftLeg, Leg rightLeg, + + float scale + ) { + super(entity, body, head, scale); + this.leftArm = leftArm; + this.rightArm = rightArm; + this.leftLeg = leftLeg; + this.rightLeg = rightLeg; + } + + @Override + protected void renderBodyParts(ShapeRenderHelper renderer) { + super.renderBodyParts(renderer); + leftArm.render(renderer, this); + rightArm.render(renderer, this); + leftLeg.render(renderer, this); + rightLeg.render(renderer, this); + } + + public float getWalkingArmSwing() { + return walkingArmSwing; + } + + public float getWalkingLegSwing() { + return walkingLegSwing; + } + + public HumanoidModel setWalkingLegSwing(float walkingLegSwing) { + this.walkingLegSwing = walkingLegSwing; + return this; + } + + public HumanoidModel setWalkingArmSwing(float walkingArmSwing) { + this.walkingArmSwing = walkingArmSwing; + return this; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java new file mode 100644 index 0000000..441ff6e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/NPedModel.java @@ -0,0 +1,284 @@ +package ru.windcorp.progressia.client.world.entity; + +import static java.lang.Math.atan2; +import static java.lang.Math.min; +import static java.lang.Math.pow; +import static java.lang.Math.toRadians; +import static ru.windcorp.progressia.common.util.FloatMathUtils.normalizeAngle; + +import glm.Glm; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import glm.vec._4.Vec4; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.Units; +import ru.windcorp.progressia.common.util.Matrices; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public abstract class NPedModel extends EntityRenderable { + + protected static abstract class BodyPart { + private final Renderable renderable; + private final Vec3 translation = new Vec3(); + + public BodyPart(Renderable renderable, Vec3 joint) { + this.renderable = renderable; + if (joint != null) { + this.translation.set(joint); + } + } + + + protected void render( + ShapeRenderHelper renderer, NPedModel model + ) { + renderer.pushTransform().translate(translation); + applyTransform(renderer.pushTransform(), model); + renderable.render(renderer); + renderer.popTransform(); + renderer.popTransform(); + } + + protected abstract void applyTransform(Mat4 mat, NPedModel model); + + public Vec3 getTranslation() { + return translation; + } + } + + public static class Body extends BodyPart { + public Body(Renderable renderable) { + super(renderable, null); + } + + @Override + protected void applyTransform(Mat4 mat, NPedModel model) { + // Do nothing + } + } + + public static class Head extends BodyPart { + private final float maxYaw; + private final float maxPitch; + + private final Vec3 viewPoint; + + public Head( + Renderable renderable, Vec3 joint, + double maxYawDegrees, double maxPitchDegrees, + Vec3 viewPoint + ) { + super(renderable, joint); + this.maxYaw = (float) toRadians(maxYawDegrees); + this.maxPitch = (float) toRadians(maxPitchDegrees); + this.viewPoint = viewPoint; + } + + @Override + protected void applyTransform(Mat4 mat, NPedModel model) { + mat.rotateZ(model.getHeadYaw()).rotateY(model.getHeadPitch()); + } + + public Vec3 getViewPoint() { + return viewPoint; + } + } + + protected final Body body; + protected final Head head; + + private float walkingParameter = 0; + private float velocityParameter = 0; + private float velocity = 0; + + /** + * If {@link #velocity} is greater than this value, {@link #velocityParameter} is 1.0. + */ + private float maxEffectiveVelocity = 5 * Units.METERS_PER_SECOND; + + /** + * If {@link #velocity} is less than {@link #maxEffectiveVelocity}, then + * {@code velocityCoeff = exp(velocity / maxEffectiveVelocity, velocityCoeffPower)}. + */ + private float velocityCoeffPower = 1; + + private final float scale; + + private float walkingFrequency; + + private float bodyYaw = Float.NaN; + private float headYaw; + private float headPitch; + + public NPedModel(EntityData data, Body body, Head head, float scale) { + super(data); + this.body = body; + this.head = head; + this.scale = scale; + } + + @Override + public void render(ShapeRenderHelper renderer) { + renderer.pushTransform().scale(scale).rotateZ(bodyYaw); + renderBodyParts(renderer); + renderer.popTransform(); + + accountForVelocity(); + evaluateAngles(); + } + + protected void renderBodyParts(ShapeRenderHelper renderer) { + body.render(renderer, this); + head.render(renderer, this); + } + + private void evaluateAngles() { + float globalYaw = normalizeAngle(getData().getYaw()); + + if (Float.isNaN(bodyYaw)) { + bodyYaw = globalYaw; + headYaw = 0; + } else { + headYaw = normalizeAngle(globalYaw - bodyYaw); + + if (headYaw > +head.maxYaw) { + bodyYaw += headYaw - +head.maxYaw; + headYaw = +head.maxYaw; + } else if (headYaw < -head.maxYaw) { + bodyYaw += headYaw - -head.maxYaw; + headYaw = -head.maxYaw; + } + } + + bodyYaw = normalizeAngle(bodyYaw); + + headPitch = Glm.clamp( + getData().getPitch(), + -head.maxPitch, head.maxPitch + ); + } + + private void accountForVelocity() { + Vec3 horizontal = Vectors.grab3(); + horizontal.set(getData().getVelocity()); + horizontal.z = 0; + + velocity = horizontal.length(); + + evaluateVelocityCoeff(); + + // TODO switch to world time + walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000; + + bodyYaw += velocityParameter * normalizeAngle( + (float) (atan2(horizontal.y, horizontal.x) - bodyYaw) + ) * min(1, GraphicsInterface.getFrameLength() * 10); + Vectors.release(horizontal); + } + + private void evaluateVelocityCoeff() { + if (velocity > maxEffectiveVelocity) { + velocityParameter = 1; + } else { + velocityParameter = (float) pow(velocity / maxEffectiveVelocity, velocityCoeffPower); + } + } + + @Override + public void getViewPoint(Vec3 output) { + Mat4 m = Matrices.grab4(); + Vec4 v = Vectors.grab4(); + + m.identity() + .scale(scale) + .rotateZ(bodyYaw) + .translate(head.getTranslation()) + .rotateZ(headYaw) + .rotateY(headPitch); + + v.set(head.getViewPoint(), 1); + m.mul(v); + + output.set(v.x, v.y, v.z); + + Vectors.release(v); + Matrices.release(m); + } + + public Body getBody() { + return body; + } + + public Head getHead() { + return head; + } + + public float getBodyYaw() { + return bodyYaw; + } + + public float getHeadYaw() { + return headYaw; + } + + public float getHeadPitch() { + return headPitch; + } + + /** + * Returns a number in the range [0; 1] that can be used to scale animation effects that depend on speed. + * This parameter is 0 when the entity is not moving and 1 when it's moving "fast". + * @return velocity parameter + */ + protected float getVelocityParameter() { + return velocityParameter; + } + + /** + * Returns a number that can be used to parameterize animation effects that depend on walking. + * This parameter increases when the entity moves (e.g. this can be total traveled distance). + * @return walking parameter + */ + protected float getWalkingParameter() { + return walkingParameter; + } + + protected float getVelocity() { + return velocity; + } + + public float getScale() { + return scale; + } + + protected float getWalkingFrequency() { + return walkingFrequency; + } + + public NPedModel setWalkingFrequency(float walkingFrequency) { + this.walkingFrequency = walkingFrequency; + return this; + } + + public float getMaxEffectiveVelocity() { + return maxEffectiveVelocity; + } + + public float getVelocityCoeffPower() { + return velocityCoeffPower; + } + + public NPedModel setMaxEffectiveVelocity(float maxEffectiveVelocity) { + this.maxEffectiveVelocity = maxEffectiveVelocity; + return this; + } + + public NPedModel setVelocityCoeffPower(float velocityCoeffPower) { + this.velocityCoeffPower = velocityCoeffPower; + return this; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java index c19a3b0..00311d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java @@ -2,86 +2,15 @@ package ru.windcorp.progressia.client.world.entity; import static java.lang.Math.*; import static ru.windcorp.progressia.common.util.FloatMathUtils.*; +import static ru.windcorp.progressia.common.util.FloatMathUtils.sin; -import glm.Glm; import glm.mat._4.Mat4; import glm.vec._3.Vec3; -import glm.vec._4.Vec4; -import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; -import ru.windcorp.progressia.common.util.Matrices; -import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.entity.EntityData; -public class QuadripedModel extends EntityRenderable { - - private static abstract class BodyPart { - private final Renderable renderable; - private final Vec3 translation = new Vec3(); - - public BodyPart(Renderable renderable, Vec3 joint) { - this.renderable = renderable; - if (joint != null) { - this.translation.set(joint); - } - } - - - protected void render( - ShapeRenderHelper renderer, QuadripedModel model - ) { - renderer.pushTransform().translate(translation); - applyTransform(renderer.pushTransform(), model); - renderable.render(renderer); - renderer.popTransform(); - renderer.popTransform(); - } - - protected abstract void applyTransform(Mat4 mat, QuadripedModel model); - - public Vec3 getTranslation() { - return translation; - } - } - - public static class Body extends BodyPart { - public Body(Renderable renderable) { - super(renderable, null); - } - - @Override - protected void applyTransform(Mat4 mat, QuadripedModel model) { - // Do nothing - } - } - - public static class Head extends BodyPart { - private final float maxYaw; - private final float maxPitch; - - private final Vec3 viewPoint; - - public Head( - Renderable renderable, Vec3 joint, - double maxYawDegrees, double maxPitchDegrees, - Vec3 viewPoint - ) { - super(renderable, joint); - this.maxYaw = (float) toRadians(maxYawDegrees); - this.maxPitch = (float) toRadians(maxPitchDegrees); - this.viewPoint = viewPoint; - } - - @Override - protected void applyTransform(Mat4 mat, QuadripedModel model) { - mat.rotateZ(model.headYaw).rotateY(model.headPitch); - } - - public Vec3 getViewPoint() { - return viewPoint; - } - } +public class QuadripedModel extends NPedModel { public static class Leg extends BodyPart { private final float animationOffset; @@ -95,33 +24,19 @@ public class QuadripedModel extends EntityRenderable { } @Override - protected void applyTransform(Mat4 mat, QuadripedModel model) { - mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); + protected void applyTransform(Mat4 mat, NPedModel model) { + float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; + float value = sin(phase); + float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter(); + mat.rotateY(value * amplitude); } + } - private final Body body; - private final Head head; private final Leg leftForeLeg, rightForeLeg; private final Leg leftHindLeg, rightHindLeg; - private final float scale; - - private float walkingAnimationParameter = 0; - private float velocityCoeff = 0; - private float velocity = 0; - - /** - * Controls how quickly velocityCoeff approaches 1 - */ - private float velocityCutoff = 10; - - private float walkingFrequency = 0.15f / 60.0f; private float walkingSwing = (float) toRadians(30); - - private float bodyYaw = Float.NaN; - private float headYaw; - private float headPitch; public QuadripedModel( EntityData entity, @@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable { float scale ) { - super(entity); + super(entity, body, head, scale); - this.body = body; - this.head = head; this.leftForeLeg = leftForeLeg; this.rightForeLeg = rightForeLeg; this.leftHindLeg = leftHindLeg; this.rightHindLeg = rightHindLeg; - - this.scale = scale; } @Override - public void render(ShapeRenderHelper renderer) { - renderer.pushTransform().scale(scale).rotateZ(bodyYaw); - body.render(renderer, this); - head.render(renderer, this); - leftForeLeg.render(renderer, this); - rightForeLeg.render(renderer, this); - leftHindLeg.render(renderer, this); - rightHindLeg.render(renderer, this); - renderer.popTransform(); - - accountForVelocity(); - evaluateAngles(); - } - - private void evaluateAngles() { - float globalYaw = normalizeAngle(getData().getYaw()); - - if (Float.isNaN(bodyYaw)) { - bodyYaw = globalYaw; - headYaw = 0; - } else { - headYaw = normalizeAngle(globalYaw - bodyYaw); - - if (headYaw > +head.maxYaw) { - bodyYaw += headYaw - +head.maxYaw; - headYaw = +head.maxYaw; - } else if (headYaw < -head.maxYaw) { - bodyYaw += headYaw - -head.maxYaw; - headYaw = -head.maxYaw; - } - } - - bodyYaw = normalizeAngle(bodyYaw); - - headPitch = Glm.clamp( - getData().getPitch(), - -head.maxPitch, head.maxPitch - ); - } - - private void accountForVelocity() { - Vec3 horizontal = Vectors.grab3(); - horizontal.set(getData().getVelocity()); - horizontal.z = 0; - - velocity = horizontal.length(); - - evaluateVelocityCoeff(); - - // TODO switch to world time - walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000; - - bodyYaw += velocityCoeff * normalizeAngle( - (float) (atan2(horizontal.y, horizontal.x) - bodyYaw) - ) * min(1, GraphicsInterface.getFrameLength() * 10); - Vectors.release(horizontal); + protected void renderBodyParts(ShapeRenderHelper renderer) { + super.renderBodyParts(renderer); + this.leftForeLeg.render(renderer, this); + this.rightForeLeg.render(renderer, this); + this.leftHindLeg.render(renderer, this); + this.rightHindLeg.render(renderer, this); } - private void evaluateVelocityCoeff() { - if (velocity * velocityCutoff > 1) { - velocityCoeff = 1; - } else { - velocityCoeff = velocity * velocityCutoff; - velocityCoeff *= velocityCoeff; - } + public float getWalkingSwing() { + return walkingSwing; } - @Override - public void getViewPoint(Vec3 output) { - Mat4 m = Matrices.grab4(); - Vec4 v = Vectors.grab4(); - - m.identity() - .scale(scale) - .rotateZ(bodyYaw) - .translate(head.getTranslation()) - .rotateZ(headYaw) - .rotateY(headPitch); - - v.set(head.getViewPoint(), 1); - m.mul(v); - - output.set(v.x, v.y, v.z); - - Vectors.release(v); - Matrices.release(m); + public QuadripedModel setWalkingSwing(float walkingSwing) { + this.walkingSwing = walkingSwing; + return this; } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 18d44b0..f25ac0a 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -80,7 +80,7 @@ public class ChunkData { TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); - Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2); + Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2).sub(getPosition()); Vec3i pos = new Vec3i(); for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { @@ -132,18 +132,28 @@ public class ChunkData { } } - EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); - javapony.setEntityId(0x42); - javapony.setPosition(new Vec3(-6, -6, 20)); - javapony.setDirection(new Vec2( - (float) Math.toRadians(40), (float) Math.toRadians(45) - )); - getEntities().add(javapony); - - EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); - statie.setEntityId(0xDEADBEEF); - statie.setPosition(new Vec3(0, 15, 16)); - getEntities().add(statie); + if (!getPosition().any()) { +// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); +// javapony.setEntityId(0x42); +// javapony.setPosition(new Vec3(-6, -6, 20)); +// javapony.setDirection(new Vec2( +// (float) Math.toRadians(40), (float) Math.toRadians(45) +// )); +// getEntities().add(javapony); + + EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); + player.setEntityId(0x42); + player.setPosition(new Vec3(-6, -6, 20)); + player.setDirection(new Vec2( + (float) Math.toRadians(40), (float) Math.toRadians(45) + )); + getEntities().add(player); + + EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); + statie.setEntityId(0xDEADBEEF); + statie.setPosition(new Vec3(0, 15, 16)); + getEntities().add(statie); + } } public BlockData getBlock(Vec3i posInChunk) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 7b24efa..1b2d16e 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -87,9 +87,14 @@ public class TestContent { } private static void registerEntities() { - registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); - register(new TestEntityRenderJavapony()); - register(new EntityLogic("Test", "Javapony")); +// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); +// register(new TestEntityRenderJavapony()); +// register(new EntityLogic("Test", "Javapony")); + + float scale = 1.8f / 8; + registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); + register(new TestEntityRenderHuman()); + register(new EntityLogic("Test", "Player")); register("Test", "Statie", TestEntityDataStatie::new); register(new TestEntityRenderStatie()); diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java new file mode 100644 index 0000000..cecfacf --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java @@ -0,0 +1,164 @@ +package ru.windcorp.progressia.test; + +import static java.lang.Math.toRadians; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.model.LambdaModel; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; +import ru.windcorp.progressia.client.graphics.model.StaticModel; +import ru.windcorp.progressia.client.graphics.texture.ComplexTexture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.world.entity.HumanoidModel; +import ru.windcorp.progressia.client.world.entity.EntityRender; +import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.common.util.FloatMathUtils; +import ru.windcorp.progressia.common.world.entity.EntityData; + +import static java.lang.Math.*; + +public class TestEntityRenderHuman extends EntityRender { + + private static final float SECOND_LAYER_OFFSET = 1 / 12f; + + private final Renderable body; + private final Renderable head; + private final Renderable leftArm; + private final Renderable rightArm; + private final Renderable leftLeg; + private final Renderable rightLeg; + + public TestEntityRenderHuman() { + super("Test", "Player"); + + ComplexTexture texture = new ComplexTexture( + EntityRenderRegistry.getEntityTexture("pyotr"), + 16, 16 + ); + + this.body = createBody(texture); + this.head = createHead(texture); + + this.leftArm = createLimb(texture, 8, 0, 12, 0, true, true); + this.rightArm = createLimb(texture, 10, 8, 10, 4, true, false); + this.leftLeg = createLimb(texture, 4, 0, 0, 0, false, true); + this.rightLeg = createLimb(texture, 0, 8, 0, 4, false, false); + } + + private Renderable createBody(ComplexTexture texture) { + return createLayeredCuboid( + texture, + 4, 8, + 4, 4, + 2, 3, 1, + -0.5f, -1, 3, + 1, 2, 3 + ); + } + + private Renderable createHead(ComplexTexture texture) { + return createLayeredCuboid( + texture, + 0, 12, + 8, 12, + 2, 2, 2, + -1, -1, 0, + 2, 2, 2 + ); + } + + private Renderable createLimb( + ComplexTexture texture, + int tx, int ty, + int tx2, int ty2, + boolean isArm, boolean isLeft + ) { + Renderable model = createLayeredCuboid( + texture, + tx, ty, + tx2, ty2, + 1, 3, 1, + -0.5f, -0.5f, isArm ? -2.5f : -3f, + 1, 1, 3 + ); + + if (isArm) { + return LambdaModel.animate( + model, + mat -> { + double phase = GraphicsInterface.getTime() + (isLeft ? 0 : Math.PI / 3); + mat.rotateX((isLeft ? +1 : -1) * 1/40f * (sin(phase) + 1)); + mat.rotateY(1/20f * sin(Math.PI / 3 * phase)); + } + ); + } else { + return model; + } + } + + private Renderable createLayeredCuboid( + ComplexTexture texture, + int tx, int ty, + int tx2, int ty2, + int tw, int th, int td, + float ox, float oy, float oz, + float sx, float sy, float sz + ) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + StaticModel.Builder b = StaticModel.builder(); + + // First layer + b.addPart(new PppBuilder( + program, + texture.getCuboidTextures(tx, ty, tw, th, td) + ).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()); + + ox -= SECOND_LAYER_OFFSET; + oy -= SECOND_LAYER_OFFSET; + oz -= SECOND_LAYER_OFFSET; + + sx += SECOND_LAYER_OFFSET * 2; + sy += SECOND_LAYER_OFFSET * 2; + sz += SECOND_LAYER_OFFSET * 2; + + // Second layer + b.addPart(new PppBuilder( + program, + texture.getCuboidTextures(tx2, ty2, tw, th, td) + ).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()); + + return new StaticModel(b); + } + + @Override + public EntityRenderable createRenderable(EntityData entity) { + return new HumanoidModel( + entity, + + new HumanoidModel.Body(body), + new HumanoidModel.Head( + head, new Vec3(0, 0, 6), 70, 25, new Vec3(1.2f, 0, 1.5f) + ), + new HumanoidModel.Arm( + leftArm, new Vec3(0, +1.5f, 3 + 3 - 0.5f), 0.0f + ), + new HumanoidModel.Arm( + rightArm, new Vec3(0, -1.5f, 3 + 3 - 0.5f), FloatMathUtils.PI_F + ), + new HumanoidModel.Leg( + leftLeg, new Vec3(0, +0.5f, 3), FloatMathUtils.PI_F + ), + new HumanoidModel.Leg( + rightLeg, new Vec3(0, -0.5f, 3), 0.0f + ), + + 1.8f / (3 + 3 + 2) + ) + .setWalkingArmSwing((float) toRadians(30)) + .setWalkingLegSwing((float) toRadians(50)) + .setWalkingFrequency(0.15f / 60.0f); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index a41a3c2..5ad784b 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -39,7 +39,7 @@ public class TestPlayerControls { private static final float FLYING_CONTROL_AUTHORITY = 0.05f; // Horizontal and vertical max control speed when walking - private static final float WALKING_SPEED = 5.0f * Units.METERS_PER_SECOND; + private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND; // (0; 1], 1 is instant change, 0 is no control authority private static final float WALKING_CONTROL_AUTHORITY = 0.1f; diff --git a/src/main/resources/assets/textures/entities/pyotr.png b/src/main/resources/assets/textures/entities/pyotr.png new file mode 100644 index 0000000000000000000000000000000000000000..a0cff2024a873ec77f1b88361042a598baf375c5 GIT binary patch literal 8036 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/textures/entities/test_skin.png b/src/main/resources/assets/textures/entities/test_skin.png new file mode 100644 index 0000000000000000000000000000000000000000..e75b5e8b2fd5b9c2dd39526c397b3a43b954e965 GIT binary patch literal 16530 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 AYybcN literal 0 HcmV?d00001 From b987412670b779cc4be8d1bcae29c989ae4f00b4 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sun, 15 Nov 2020 21:11:07 +0300 Subject: [PATCH 13/24] Added StringUtil.replaceAll --- .../ru/windcorp/jputil/chars/StringUtil.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java index 9a8933b..8641ccf 100644 --- a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java +++ b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.Objects; import java.util.function.IntFunction; public class StringUtil { @@ -775,4 +776,35 @@ public class StringUtil { else return (char) ('A' - 0xA + value); } + public static String replaceAll(String source, String substring, String replacement) { + Objects.requireNonNull(source, "source"); + Objects.requireNonNull(substring, "substring"); + + if (substring.isEmpty()) { + throw new IllegalArgumentException("substring is empty"); + } + + if (!source.contains(substring)) { // also passes if source is empty + return source; + } + + if (substring.equals(replacement)) { // null-safe + return source; + } + + StringBuilder sb = new StringBuilder(2 * source.length()); + + for (int i = 0; i < source.length() - substring.length() + 1; ++i) { + if (source.startsWith(substring, i)) { + if (replacement != null) { + sb.append(replacement); + } + } else { + sb.append(source.charAt(i)); + } + } + + return sb.toString(); + } + } From ec6181aaa83972acc786b61b6ae485e1667b3353 Mon Sep 17 00:00:00 2001 From: serega404 Date: Sun, 15 Nov 2020 23:11:46 +0300 Subject: [PATCH 14/24] Crash reports handle invalid format strings properly --- .../common/util/crash/CrashReports.java | 310 ++++++++++-------- 1 file changed, 165 insertions(+), 145 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java index ce4cdee..f6dd3d2 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java +++ b/src/main/java/ru/windcorp/progressia/common/util/crash/CrashReports.java @@ -20,193 +20,213 @@ import java.util.*; public class CrashReports { - private CrashReports() { - } + private CrashReports() { + } - private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); + private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports"); - private static final Collection PROVIDERS = Collections.synchronizedCollection(new ArrayList<>()); + private static final Collection PROVIDERS = Collections.synchronizedCollection(new ArrayList<>()); - private static final Collection ANALYZERS = Collections.synchronizedCollection(new ArrayList<>()); + private static final Collection ANALYZERS = Collections.synchronizedCollection(new ArrayList<>()); - private static final Logger LOGGER = LogManager.getLogger("crash"); + private static final Logger LOGGER = LogManager.getLogger("crash"); - /** - * This method never returns. - *

- * TODO document - * - * @param throwable - * @param messageFormat - * @param args - */ - public static void report(Throwable throwable, String messageFormat, Object... args) { - StringBuilder output = new StringBuilder(); + /** + * This method never returns. + *

+ * TODO document + * + * @param throwable + * @param messageFormat + * @param args + */ + public static void report(Throwable throwable, String messageFormat, Object... args) { + StringBuilder output = new StringBuilder(); - appendContextProviders(output); - addSeparator(output); - if (appendAnalyzers(output, throwable, messageFormat, args)) { - addSeparator(output); - } + try { + String.format(messageFormat, args); + } catch (IllegalFormatException e) { + messageFormat = StringUtil.replaceAll(messageFormat, "%", "%%"); - appendMessageFormat(output, messageFormat, args); + if (args.length != 0) { + messageFormat += "\nArgs:"; + for (Object arg : args) { + try { + messageFormat += " \"" + arg.toString() + "\""; + } catch (Throwable t) { + messageFormat += " exc: \"" + t.getClass().toString() + "\""; + } + } + args = new Object[0]; // clear args + } - appendStackTrace(output, throwable); + messageFormat += "\nCould not format provided description"; + } - export(output.toString()); + appendContextProviders(output); + addSeparator(output); + if (appendAnalyzers(output, throwable, messageFormat, args)) { + addSeparator(output); + } - System.exit(0); - } + appendMessageFormat(output, messageFormat, args); - private static void appendContextProviders(StringBuilder output) { + appendStackTrace(output, throwable); - // Do a local copy to avoid deadlocks -OLEGSHA - ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); + export(output.toString()); - for (ContextProvider provider : localProvidersCopy) { - if (provider == null) - continue; + System.exit(0); + } - addSeparator(output); + private static void appendContextProviders(StringBuilder output) { - try { - Map buf = new HashMap<>(); - provider.provideContext(buf); + // Do a local copy to avoid deadlocks -OLEGSHA + ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]); - if (!buf.isEmpty()) { - output.append("Provider name: ").append(provider.getName()).append("\n"); - for (Map.Entry entry : buf.entrySet()) { - output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); - } - } - } catch (Throwable t) { - output.append("\n"); + for (ContextProvider provider : localProvidersCopy) { + if (provider == null) + continue; - String providerName; + addSeparator(output); - try { - providerName = provider.getName(); - } catch (Throwable t1) { - providerName = provider.getClass().getName(); - } + try { + Map buf = new HashMap<>(); + provider.provideContext(buf); - output.append(providerName).append(" is broken").append("\n"); - // ContextProvider is broken - } - } - } + if (!buf.isEmpty()) { + output.append("Provider name: ").append(provider.getName()).append("\n"); + for (Map.Entry entry : buf.entrySet()) { + output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); + } + } + } catch (Throwable t) { + output.append("\n"); - private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat, - Object[] args) { - boolean analyzerResponsesExist = false; + String providerName; - // Do a local copy to avoid deadlocks -OLEGSHA - Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); + try { + providerName = provider.getName(); + } catch (Throwable t1) { + providerName = provider.getClass().getName(); + } - for (Analyzer analyzer : localAnalyzersCopy) { - if (analyzer == null) - continue; + output.append(providerName).append(" is broken").append("\n"); + // ContextProvider is broken + } + } + } - String answer; - try { - answer = analyzer.analyze(throwable, messageFormat, args); + private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat, + Object[] args) { + boolean analyzerResponsesExist = false; - if (answer != null && !answer.isEmpty()) { - analyzerResponsesExist = true; - output.append(analyzer.getName()).append(": ").append(answer).append("\n"); - } - } catch (Throwable t) { - analyzerResponsesExist = true; + // Do a local copy to avoid deadlocks -OLEGSHA + Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]); - output.append("\n"); + for (Analyzer analyzer : localAnalyzersCopy) { + if (analyzer == null) + continue; - String analyzerName; + String answer; + try { + answer = analyzer.analyze(throwable, messageFormat, args); - try { - analyzerName = analyzer.getName(); - } catch (Throwable t1) { - analyzerName = analyzer.getClass().getName(); - } + if (answer != null && !answer.isEmpty()) { + analyzerResponsesExist = true; + output.append(analyzer.getName()).append(": ").append(answer).append("\n"); + } + } catch (Throwable t) { + analyzerResponsesExist = true; - output.append(analyzerName).append(" is broken").append("\n"); - // Analyzer is broken - } - } + output.append("\n"); - return analyzerResponsesExist; - } + String analyzerName; - private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { - output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n"); + try { + analyzerName = analyzer.getName(); + } catch (Throwable t1) { + analyzerName = analyzer.getClass().getName(); + } - addSeparator(output); - } + output.append(analyzerName).append(" is broken").append("\n"); + // Analyzer is broken + } + } - private static void appendStackTrace(StringBuilder output, Throwable throwable) { - output.append("Stacktrace: \n"); + return analyzerResponsesExist; + } - if (throwable == null) { - output.append("no Throwable provided").append("\n"); - return; - } + private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) { + output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n"); - // Formatting to a human-readable string - Writer sink = new StringBuilderWriter(output); - try { - throwable.printStackTrace(new PrintWriter(sink)); - } catch (Exception e) { - // PLAK - } - output.append("\n"); - } + addSeparator(output); + } - private static void export(String report) { - try { - LOGGER.fatal("/n" + report); - } catch (Exception e) { - // PLAK - } + private static void appendStackTrace(StringBuilder output, Throwable throwable) { + output.append("Stacktrace: \n"); - System.err.println(report); + if (throwable == null) { + output.append("no Throwable provided").append("\n"); + return; + } - generateCrashReportFiles(report); - } + // Formatting to a human-readable string + Writer sink = new StringBuilderWriter(output); + try { + throwable.printStackTrace(new PrintWriter(sink)); + } catch (Exception e) { + // PLAK + } + output.append("\n"); + } - private static void generateCrashReportFiles(String output) { - Date date = new Date(); - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); + private static void export(String report) { + try { + LOGGER.fatal("/n" + report); + } catch (Exception e) { + // PLAK + } - try { - if (!Files.exists(CRASH_REPORTS_PATH)) - Files.createDirectory(CRASH_REPORTS_PATH); + System.err.println(report); - createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); - createFileForCrashReport(output, - CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); - } catch (Throwable t) { - // Crash Report not created - } - } + generateCrashReportFiles(report); + } - private static void createFileForCrashReport(String buffer, String filename) { - try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) { - writer.write(buffer); - } catch (IOException ex) { - // Crash Report not created - } - } + private static void generateCrashReportFiles(String output) { + Date date = new Date(); + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss"); - public static void registerProvider(ContextProvider provider) { - PROVIDERS.add(provider); - } + try { + if (!Files.exists(CRASH_REPORTS_PATH)) + Files.createDirectory(CRASH_REPORTS_PATH); - public static void registerAnalyzer(Analyzer analyzer) { - ANALYZERS.add(analyzer); - } + createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log"); + createFileForCrashReport(output, + CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log"); + } catch (Throwable t) { + // Crash Report not created + } + } - private static void addSeparator(StringBuilder sb) { - sb.append( - // 80 chars - "--------------------------------------------------------------------------------").append("\n"); - } + private static void createFileForCrashReport(String buffer, String filename) { + try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) { + writer.write(buffer); + } catch (IOException ex) { + // Crash Report not created + } + } + + public static void registerProvider(ContextProvider provider) { + PROVIDERS.add(provider); + } + + public static void registerAnalyzer(Analyzer analyzer) { + ANALYZERS.add(analyzer); + } + + private static void addSeparator(StringBuilder sb) { + sb.append( + // 80 chars + "--------------------------------------------------------------------------------").append("\n"); + } } From b51b3a4d800a454fb3f3081686b9bd3c2ecb277d Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Mon, 16 Nov 2020 18:02:41 +0300 Subject: [PATCH 15/24] Blocks looked at are now highlighted --- .../ru/windcorp/progressia/client/Client.java | 7 +- .../client/graphics/world/LayerWorld.java | 20 +- .../client/graphics/world/LocalPlayer.java | 64 ++++++ .../progressia/common/util/VectorUtil.java | 208 ++++++++++++++++++ .../progressia/common/world/BlockRay.java | 123 +++++++++++ .../progressia/common/world/ChunkData.java | 8 - .../progressia/common/world/Player.java | 17 ++ .../common/world/entity/EntityData.java | 10 + .../test/CollisionModelRenderer.java | 2 +- .../windcorp/progressia/test/TestContent.java | 24 +- .../progressia/test/TestPlayerControls.java | 13 +- 11 files changed, 475 insertions(+), 21 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/BlockRay.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/Player.java diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index de6fa54..cd8c5a4 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -3,6 +3,7 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; +import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -10,7 +11,7 @@ import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { private final WorldRender world; - private EntityData localPlayer; + private LocalPlayer localPlayer; private final Camera camera = new Camera((float) Math.toRadians(70)); @@ -27,12 +28,12 @@ public class Client { return world; } - public EntityData getLocalPlayer() { + public LocalPlayer getLocalPlayer() { return localPlayer; } public void setLocalPlayer(EntityData localPlayer) { - this.localPlayer = localPlayer; + this.localPlayer = new LocalPlayer(localPlayer); } public Camera getCamera() { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index fb7557e..2e4b6be 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.client.graphics.world; import java.util.ArrayList; import java.util.List; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.comms.controls.InputBasedControls; @@ -65,6 +66,10 @@ public class LayerWorld extends Layer { if (camera.hasAnchor()) { renderWorld(); } + + if (client.getLocalPlayer() != null) { + client.getLocalPlayer().update(client.getWorld()); + } } private void renderWorld() { @@ -89,6 +94,7 @@ public class LayerWorld extends Layer { try { tmp_performCollisions(tickLength); + tmp_drawSelectionBox(); tmp_testControls.applyPlayerControls(); @@ -122,13 +128,25 @@ public class LayerWorld extends Layer { ); } + private void tmp_drawSelectionBox() { + LocalPlayer player = client.getLocalPlayer(); + if (player == null) return; + + Vec3i lookingAt = player.getLookingAt(); + if (lookingAt == null) return; + + helper.pushTransform().translate(lookingAt.x, lookingAt.y, lookingAt.z).scale(1.1f); + CollisionModelRenderer.renderCollisionModel(client.getWorld().getData().getCollisionModelOfBlock(lookingAt), helper); + helper.popTransform(); + } + private void tmp_applyFriction(EntityData entity) { final float frictionCoeff = 1 - 1e-5f; entity.getVelocity().mul(frictionCoeff); } private void tmp_applyGravity(EntityData entity, float tickLength) { - if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) { + if (ClientState.getInstance().getLocalPlayer().getEntity() == entity && tmp_testControls.isFlying()) { return; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java new file mode 100644 index 0000000..5df2b84 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java @@ -0,0 +1,64 @@ +package ru.windcorp.progressia.client.graphics.world; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.world.WorldRender; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.BlockRay; +import ru.windcorp.progressia.common.world.Player; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class LocalPlayer extends Player { + + private Vec3i lookingAt = new Vec3i(); + private boolean isLookingAtBlock = false; + + private BlockRay lookingAtRay = new BlockRay(); + + public LocalPlayer(EntityData entity) { + super(entity); + } + + public Vec3i getLookingAt() { + return isLookingAtBlock ? lookingAt : null; + } + + public void update(WorldRender world) { + updateLookingAt(world); + } + + private void updateLookingAt(WorldRender world) { + Vec3 direction = Vectors.grab3(); + Vec3 start = Vectors.grab3(); + + BlockRay ray = lookingAtRay; + EntityData player = getEntity(); + + player.getLookingAtVector(direction); + world.getEntityRenderable(player).getViewPoint(start); + start.add(player.getPosition()); + + isLookingAtBlock = false; + + for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) { + Vec3i blockInWorld = ray.current(); + + if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) { + isLookingAtBlock = true; + lookingAt.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + break; + } + } + + ray.end(); + + Vectors.release(direction); + Vectors.release(start); + } + + public EntityRenderable getRenderable(WorldRender world) { + return world.getEntityRenderable(getEntity()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java index 712a9cf..2b42481 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -3,12 +3,22 @@ package ru.windcorp.progressia.common.util; import java.util.function.Consumer; import glm.mat._4.Mat4; +import glm.vec._2.Vec2; +import glm.vec._2.d.Vec2d; +import glm.vec._2.i.Vec2i; import glm.vec._3.Vec3; +import glm.vec._3.d.Vec3d; import glm.vec._3.i.Vec3i; import glm.vec._4.Vec4; +import glm.vec._4.d.Vec4d; +import glm.vec._4.i.Vec4i; public class VectorUtil { + public static enum Axis { + X, Y, Z, W; + } + public static void forEachVectorInCuboid( int x0, int y0, int z0, int x1, int y1, int z1, @@ -47,6 +57,204 @@ public class VectorUtil { inOut.set(vec4.x, vec4.y, vec4.z); } + public static Vec3 linearCombination( + Vec3 va, float ka, + Vec3 vb, float kb, + Vec3 output + ) { + output.set( + va.x * ka + vb.x * kb, + va.y * ka + vb.y * kb, + va.z * ka + vb.z * kb + ); + return output; + } + + public static Vec3 linearCombination( + Vec3 va, float ka, + Vec3 vb, float kb, + Vec3 vc, float kc, + Vec3 output + ) { + output.set( + va.x * ka + vb.x * kb + vc.x * kc, + va.y * ka + vb.y * kb + vc.y * kc, + va.z * ka + vb.z * kb + vc.z * kc + ); + return output; + } + + public static float get(Vec2 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2 does not have axis " + a); + } + } + + public static Vec2 set(Vec2 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2 does not have axis " + a); + } + return v; + } + + public static int get(Vec2i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2i does not have axis " + a); + } + } + + public static Vec2i set(Vec2i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2i does not have axis " + a); + } + return v; + } + + public static double get(Vec2d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + default: throw new IllegalArgumentException("Vec2d does not have axis " + a); + } + } + + public static Vec2d set(Vec2d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + default: throw new IllegalArgumentException("Vec2d does not have axis " + a); + } + return v; + } + + public static float get(Vec3 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3 does not have axis " + a); + } + } + + public static Vec3 set(Vec3 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3 does not have axis " + a); + } + return v; + } + + public static int get(Vec3i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3i does not have axis " + a); + } + } + + public static Vec3i set(Vec3i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3i does not have axis " + a); + } + return v; + } + + public static double get(Vec3d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + default: throw new IllegalArgumentException("Vec3d does not have axis " + a); + } + } + + public static Vec3d set(Vec3d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + default: throw new IllegalArgumentException("Vec3d does not have axis " + a); + } + return v; + } + + public static float get(Vec4 v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4 does not have axis " + a); + } + } + + public static Vec4 set(Vec4 v, Axis a, float value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4 does not have axis " + a); + } + return v; + } + + public static int get(Vec4i v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4i does not have axis " + a); + } + } + + public static Vec4i set(Vec4i v, Axis a, int value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4i does not have axis " + a); + } + return v; + } + + public static double get(Vec4d v, Axis a) { + switch (a) { + case X: return v.x; + case Y: return v.y; + case Z: return v.z; + case W: return v.w; + default: throw new IllegalArgumentException("Vec4d does not have axis " + a); + } + } + + public static Vec4d set(Vec4d v, Axis a, double value) { + switch (a) { + case X: v.x = value; break; + case Y: v.y = value; break; + case Z: v.z = value; break; + case W: v.w = value; break; + default: throw new IllegalArgumentException("Vec4d does not have axis " + a); + } + return v; + } + private VectorUtil() {} } diff --git a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java new file mode 100644 index 0000000..158a1fc --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java @@ -0,0 +1,123 @@ +package ru.windcorp.progressia.common.world; + +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.VectorUtil; +import ru.windcorp.progressia.common.util.VectorUtil.Axis; + +import static java.lang.Math.*; + +public class BlockRay { + + private final Vec3 position = new Vec3(); + private final Vec3 direction = new Vec3(); + + private float distance; + + private final Vec3i block = new Vec3i(); + + private boolean isValid = false; + + public void start(Vec3 position, Vec3 direction) { + if (!direction.any()) { + throw new IllegalArgumentException("Direction is a zero vector"); + } + + isValid = true; + this.position.set(position).sub(0.5f); // Make sure lattice points are block vertices, not centers + this.direction.set(direction).normalize(); + this.block.set(toBlock(position.x), toBlock(position.y), toBlock(position.z)); + this.distance = 0; + } + + public void end() { + isValid = false; + } + + public Vec3i next() { + checkState(); + + float tx = distanceToEdge(position.x, direction.x); + float ty = distanceToEdge(position.y, direction.y); + float tz = distanceToEdge(position.z, direction.z); + + float tMin; + Axis axis; + + if (tx < ty && tx < tz) { + tMin = tx; + axis = Axis.X; + } else if (ty < tx && ty < tz) { + tMin = ty; + axis = Axis.Y; + } else { + tMin = tz; + axis = Axis.Z; + } + + // block.(axis) += signum(direction.(axis)) + VectorUtil.set(block, axis, VectorUtil.get(block, axis) + (int) signum(VectorUtil.get(direction, axis))); + + // position += direction * tMin + VectorUtil.linearCombination(position, 1, direction, tMin, position); // position += direction * tMin + distance += tMin; + + // position.(axis) = round(position.(axis)) + VectorUtil.set(position, axis, round(VectorUtil.get(position, axis))); + + return block; + } + + public Vec3i current() { + checkState(); + return block; + } + + private static float distanceToEdge(float c, float dir) { + if (dir == 0) return Float.POSITIVE_INFINITY; + + float edge; + + if (dir > 0) { + edge = strictCeil(c); + } else { + edge = strictFloor(c); + } + + return (edge - c) / dir; + } + + public float getDistance() { + checkState(); + return distance; + } + + private void checkState() { + if (!isValid) { + throw new IllegalStateException("BlockRay not started"); + } + } + + private static int toBlock(float c) { + return (int) round(c); + } + + /** + * Returns a smallest integer a such that a > c. + * @param c the number to compute strict ceiling of + * @return the strict ceiling of c + */ + private static float strictCeil(float c) { + return (float) (floor(c) + 1); + } + + /** + * Returns a largest integer a such that a < c. + * @param c the number to compute strict ceiling of + * @return the strict ceiling of c + */ + private static float strictFloor(float c) { + return (float) (ceil(c) - 1); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index f25ac0a..031be35 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -133,14 +133,6 @@ public class ChunkData { } if (!getPosition().any()) { -// EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); -// javapony.setEntityId(0x42); -// javapony.setPosition(new Vec3(-6, -6, 20)); -// javapony.setDirection(new Vec2( -// (float) Math.toRadians(40), (float) Math.toRadians(45) -// )); -// getEntities().add(javapony); - EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); player.setEntityId(0x42); player.setPosition(new Vec3(-6, -6, 20)); diff --git a/src/main/java/ru/windcorp/progressia/common/world/Player.java b/src/main/java/ru/windcorp/progressia/common/world/Player.java new file mode 100644 index 0000000..14d8c76 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/Player.java @@ -0,0 +1,17 @@ +package ru.windcorp.progressia.common.world; + +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class Player { + + private EntityData entity; + + public Player(EntityData entity) { + this.entity = entity; + } + + public EntityData getEntity() { + return entity; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index 0b3b73c..5cd79fd 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -119,5 +119,15 @@ public class EntityData extends StatefulObject implements Collideable { public void changeVelocityOnCollision(Vec3 velocityChange) { getVelocity().add(velocityChange); } + + public Vec3 getLookingAtVector(Vec3 output) { + output.set( + Math.cos(getPitch()) * Math.cos(getYaw()), + Math.cos(getPitch()) * Math.sin(getYaw()), + -Math.sin(getPitch()) + ); + + return output; + } } diff --git a/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java index 15463d4..e2d226f 100644 --- a/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java +++ b/src/main/java/ru/windcorp/progressia/test/CollisionModelRenderer.java @@ -53,7 +53,7 @@ public class CollisionModelRenderer { } public static void renderBlock(Vec3i coords, ShapeRenderHelper helper) { - helper.pushTransform().translate(coords.x, coords.y, coords.z); + helper.pushTransform().translate(coords.x, coords.y, coords.z).scale(0.25f); CUBE_GRAY.render(helper); helper.popTransform(); } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 1b2d16e..3074815 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -87,10 +87,6 @@ public class TestContent { } private static void registerEntities() { -// registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); -// register(new TestEntityRenderJavapony()); -// register(new EntityLogic("Test", "Javapony")); - float scale = 1.8f / 8; registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); register(new TestEntityRenderHuman()); @@ -121,6 +117,26 @@ public class TestContent { server.getAdHocChanger().setBlock(z000, block); } }); + +// ControlDataRegistry.getInstance().register(new ControlData("Test", "BreakBlock")); +// ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "BreakBlock", new KeyMatcher(GLFW.GLFW_KEY_ENTER, new int[0], 0)::matches)); +// ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "BreakBlock") { +// @Override +// public void apply(Server server, PacketControl packet, Client client) { +// Vec3i z000 = new Vec3i(0, 0, 0); +// +// ChunkData data = server.getWorld().getChunk(z000).getData(); +// +// BlockData block; +// if (data.getBlock(z000).getId().equals("Test:Stone")) { +// block = BlockDataRegistry.getInstance().get("Test:Glass"); +// } else { +// block = BlockDataRegistry.getInstance().get("Test:Stone"); +// } +// +// server.getAdHocChanger().setBlock(z000, block); +// } +// }); } private static void register(BlockData x) { diff --git a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java index 5ad784b..6282145 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java +++ b/src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java @@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.util.Matrices; @@ -65,7 +66,7 @@ public class TestPlayerControls { return; } - EntityData player = getPlayer(); + EntityData player = getEntity(); Mat3 angMat = Matrices.grab3(); angMat.identity().rotateZ(player.getYaw()); @@ -185,7 +186,7 @@ public class TestPlayerControls { } private void jump() { - getPlayer().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); + getEntity().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); } private void handleShift(int multiplier) { @@ -223,7 +224,7 @@ public class TestPlayerControls { final float yawScale = -0.002f; final float pitchScale = yawScale; - EntityData player = getPlayer(); + EntityData player = getEntity(); normalizeAngles(player.getDirection().add( (float) (event.getChangeX() * yawScale), @@ -241,7 +242,11 @@ public class TestPlayerControls { ); } - private EntityData getPlayer() { + private EntityData getEntity() { + return getPlayer().getEntity(); + } + + private LocalPlayer getPlayer() { return ClientState.getInstance().getLocalPlayer(); } From 5d7cfdb3bca3db311dc50fb568a56ef2794460cf Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 17 Nov 2020 12:46:33 +0300 Subject: [PATCH 16/24] Refactored Namespaced stuff - Namespaced now takes full ID in constructor - Changed the rest of source accordingly - Moved everything related to Namespaced into .util.namespaced package - Errors are now reported with IllegalIdException instead of IAE - Error checking is optimized - NamespacedUtil exposes more of its requirements - Added StringUtil.splitAt - Added ArrayUtil.isSorted --- .../java/ru/windcorp/jputil/ArrayUtil.java | 98 ++++++++++++++++ .../ru/windcorp/jputil/chars/StringUtil.java | 102 +++++++++++++++++ .../client/comms/controls/ControlTrigger.java | 6 +- .../controls/ControlTriggerInputBased.java | 4 +- .../controls/ControlTriggerOnKeyPress.java | 7 +- .../controls/ControlTriggerRegistry.java | 2 +- .../client/world/block/BlockRender.java | 6 +- .../client/world/block/BlockRenderNone.java | 4 +- .../world/block/BlockRenderOpaqueCube.java | 11 +- .../world/block/BlockRenderRegistry.java | 2 +- .../world/block/BlockRenderTexturedCube.java | 4 +- .../block/BlockRenderTransparentCube.java | 11 +- .../cro/ChunkRenderOptimizerSupplier.java | 10 +- .../world/cro/ChunkRenderOptimizers.java | 2 +- .../client/world/entity/EntityRender.java | 6 +- .../world/entity/EntityRenderRegistry.java | 2 +- .../client/world/tile/TileRender.java | 6 +- .../client/world/tile/TileRenderGrass.java | 4 +- .../client/world/tile/TileRenderRegistry.java | 2 +- .../client/world/tile/TileRenderSimple.java | 4 +- .../common/comms/controls/ControlData.java | 6 +- .../comms/controls/ControlDataRegistry.java | 2 +- .../common/comms/controls/PacketControl.java | 4 +- .../common/comms/packets/Packet.java | 6 +- .../comms/packets/PacketSetLocalPlayer.java | 6 +- .../comms/packets/PacketWorldChange.java | 4 +- .../state/InspectingStatefulObjectLayout.java | 18 +-- .../common/state/IntStateField.java | 4 +- .../state/OptimizedStatefulObjectLayout.java | 2 +- .../progressia/common/state/StateField.java | 6 +- .../common/state/StatefulObject.java | 11 +- .../common/state/StatefulObjectLayout.java | 2 +- .../common/state/StatefulObjectRegistry.java | 12 +- .../common/util/NamespacedUtil.java | 46 -------- .../util/namespaces/IllegalIdException.java | 27 +++++ .../util/{ => namespaces}/Namespaced.java | 52 ++------- .../{ => namespaces}/NamespacedRegistry.java | 2 +- .../util/namespaces/NamespacedUtil.java | 106 ++++++++++++++++++ .../common/world/block/BlockData.java | 6 +- .../common/world/block/BlockDataRegistry.java | 2 +- .../common/world/entity/EntityData.java | 4 +- .../world/entity/EntityDataRegistry.java | 4 +- .../world/entity/PacketEntityChange.java | 6 +- .../common/world/tile/TileData.java | 6 +- .../common/world/tile/TileDataRegistry.java | 2 +- .../server/comms/controls/ControlLogic.java | 6 +- .../comms/controls/ControlLogicRegistry.java | 2 +- .../world/ImplementedChangeTracker.java | 12 +- .../server/world/block/BlockLogic.java | 6 +- .../world/block/BlockLogicRegistry.java | 2 +- .../server/world/entity/EntityLogic.java | 6 +- .../world/entity/EntityLogicRegistry.java | 2 +- .../server/world/tile/TileLogic.java | 6 +- .../server/world/tile/TileLogicRegistry.java | 2 +- .../windcorp/progressia/test/TestContent.java | 104 +++++++---------- .../progressia/test/TestEntityDataStatie.java | 8 +- .../test/TestEntityLogicStatie.java | 4 +- .../test/TestEntityRenderHuman.java | 19 +++- .../test/TestEntityRenderJavapony.java | 4 +- .../test/TestEntityRenderStatie.java | 4 +- .../jputil/chars/stringUtil/SplitAtTest.java | 70 ++++++++++++ .../progressia/util/NamespacedTest.java | 42 +++---- 62 files changed, 635 insertions(+), 303 deletions(-) delete mode 100644 src/main/java/ru/windcorp/progressia/common/util/NamespacedUtil.java create mode 100644 src/main/java/ru/windcorp/progressia/common/util/namespaces/IllegalIdException.java rename src/main/java/ru/windcorp/progressia/common/util/{ => namespaces}/Namespaced.java (51%) rename src/main/java/ru/windcorp/progressia/common/util/{ => namespaces}/NamespacedRegistry.java (92%) create mode 100644 src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedUtil.java create mode 100644 src/test/java/ru/windcorp/jputil/chars/stringUtil/SplitAtTest.java diff --git a/src/main/java/ru/windcorp/jputil/ArrayUtil.java b/src/main/java/ru/windcorp/jputil/ArrayUtil.java index 4c849b7..81d29a3 100644 --- a/src/main/java/ru/windcorp/jputil/ArrayUtil.java +++ b/src/main/java/ru/windcorp/jputil/ArrayUtil.java @@ -66,6 +66,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(byte[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(short[] array, short element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -107,6 +119,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(short[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(int[] array, int element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -148,6 +172,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(int[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(long[] array, long element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -189,6 +225,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(long[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(float[] array, float element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -229,6 +277,18 @@ public class ArrayUtil { return -1; } + + public static boolean isSorted(float[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } public static int firstIndexOf(double[] array, double element) { for (int i = 0; i < array.length; ++i) { @@ -271,6 +331,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(double[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(boolean[] array, boolean element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -340,6 +412,18 @@ public class ArrayUtil { return -1; } + public static boolean isSorted(char[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + if ((array[i] < array[i + 1]) != ascending) { + return false; + } + } + + return true; + } + public static int firstIndexOf(Object[] array, Object element) { for (int i = 0; i < array.length; ++i) { if (array[i] == element) { @@ -422,6 +506,20 @@ public class ArrayUtil { return -1; } + public static > boolean isSorted(T[] array, boolean ascending) { + for (int i = 0; i < array.length - 1; ++i) { + if (array[i] == array[i + 1]) continue; + + int order = array[i].compareTo(array[i + 1]); + + if ((order < 0) != ascending) { + return false; + } + } + + return true; + } + public static long sum(byte[] array, int start, int length) { long s = 0; length += start; diff --git a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java index 8641ccf..de5c37b 100644 --- a/src/main/java/ru/windcorp/jputil/chars/StringUtil.java +++ b/src/main/java/ru/windcorp/jputil/chars/StringUtil.java @@ -29,6 +29,8 @@ import java.util.Iterator; import java.util.Objects; import java.util.function.IntFunction; +import ru.windcorp.jputil.ArrayUtil; + public class StringUtil { private StringUtil() {} @@ -369,6 +371,106 @@ public class StringUtil { return result; } + /** + * Splits {@code src} at index {@code at} discarding the character at that index. + *

+ * Indices {@code 0} and {@code src.length() - 1} produce {@code str} excluding + * the specified character and {@code ""}. + *

+ * @param src the String to split + * @param at index to split at + * @throws IllegalArgumentException if the index is out of bounds for {@code src} + * @return an array containing the substrings, in order of encounter in {@code src}. + * Its length is always 2. + */ + public static String[] splitAt(String src, int at) { + Objects.requireNonNull(src, "src"); + + if (at < 0) { + throw new StringIndexOutOfBoundsException(at); + } else if (at >= src.length()) { + throw new StringIndexOutOfBoundsException(at); + } + + if (at == 0) { + return new String[] {"", src.substring(1)}; + } else if (at == src.length()) { + return new String[] {src.substring(0, src.length() - 1), ""}; + } + + return new String[] { + src.substring(0, at), + src.substring(at + 1) + }; + } + + /** + * Splits {@code src} at indices {@code at} discarding characters at those indices. + *

+ * Indices {@code 0} and {@code src.length() - 1} produce extra zero-length outputs. + * Duplicate indices produce extra zero-length outputs. + *

+ * Examples: + *

+	 * splitAt("a.b.c", new int[] {1, 3})    -> {"a", "b", "c"}
+	 * splitAt("a..b",  new int[] {1, 2})    -> {"a", "", "b"}
+	 * splitAt(".b.",   new int[] {0, 2})    -> {"", "b", ""}
+	 * splitAt("a.b",   new int[] {1, 1, 1}) -> {"a", "", "", "b"}
+	 * 
+ * @param src the String to split + * @param at indices to split at, in any order + * @throws IllegalArgumentException if some index is out of bounds for {@code src} + * @return an array containing the substrings, in order of encounter in {@code src}. + * Its length is always {@code at.length + 1}. + */ + public static String[] splitAt(String src, int... at) { + Objects.requireNonNull(src, "src"); + Objects.requireNonNull(at, "at"); + + if (at.length == 0) return new String[] {src}; + if (at.length == 1) return splitAt(src, at[0]); + + int[] indices; // Always sorted + + if (ArrayUtil.isSorted(at, true)) { + indices = at; + } else { + indices = at.clone(); + Arrays.sort(indices); + } + + if (indices[0] < 0) { + throw new StringIndexOutOfBoundsException(indices[0]); + } else if (indices[indices.length - 1] >= src.length()) { + throw new StringIndexOutOfBoundsException(indices[indices.length - 1]); + } + + String[] result = new String[at.length + 1]; + + int start = 0; + int resultIndex = 0; + for (int index : indices) { + int end = index; + + String substring; + + if (end <= start) { + // Duplicate or successive index + substring = ""; + } else { + substring = src.substring(start, end); + } + + result[resultIndex] = substring; + resultIndex++; + start = end + 1; + } + + result[resultIndex] = src.substring(start); + + return result; + } + private static IllegalArgumentException illegalArrayLength(int length) { return new IllegalArgumentException("arrayLength must be non-negative (" + length + ")"); } diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java index b755284..3c1ca37 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java @@ -1,11 +1,11 @@ package ru.windcorp.progressia.client.comms.controls; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public abstract class ControlTrigger extends Namespaced { - public ControlTrigger(String namespace, String name) { - super(namespace, name); + public ControlTrigger(String id) { + super(id); } } diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java index abdc3a4..eeec0f0 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java @@ -5,8 +5,8 @@ import ru.windcorp.progressia.common.comms.controls.PacketControl; public abstract class ControlTriggerInputBased extends ControlTrigger { - public ControlTriggerInputBased(String namespace, String name) { - super(namespace, name); + public ControlTriggerInputBased(String id) { + super(id); } public abstract PacketControl onInputEvent(InputEvent event); diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java index c830318..746cb9f 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java @@ -6,6 +6,7 @@ import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil; public class ControlTriggerOnKeyPress extends ControlTriggerInputBased { @@ -13,13 +14,13 @@ public class ControlTriggerOnKeyPress extends ControlTriggerInputBased { private final PacketControl packet; public ControlTriggerOnKeyPress( - String namespace, String name, + String id, Predicate predicate ) { - super(namespace, name); + super(id); this.predicate = predicate; this.packet = new PacketControl( - getNamespace(), "ControlKeyPress" + getName(), + NamespacedUtil.getId(getNamespace(), "ControlKeyPress" + getName()), ControlDataRegistry.getInstance().get(getId()) ); } diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java index b695325..d0c01c8 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.client.comms.controls; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class ControlTriggerRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java index 4f39b05..b7e8de5 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRender.java @@ -18,13 +18,13 @@ package ru.windcorp.progressia.client.world.block; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.client.graphics.model.Renderable; -import ru.windcorp.progressia.common.util.Namespaced; public abstract class BlockRender extends Namespaced { - public BlockRender(String namespace, String name) { - super(namespace, name); + public BlockRender(String id) { + super(id); } public void render(ShapeRenderHelper renderer) { diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderNone.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderNone.java index 590ec31..24db070 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderNone.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderNone.java @@ -22,8 +22,8 @@ import ru.windcorp.progressia.client.graphics.model.Renderable; public class BlockRenderNone extends BlockRender { - public BlockRenderNone(String namespace, String name) { - super(namespace, name); + public BlockRenderNone(String id) { + super(id); } @Override diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderOpaqueCube.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderOpaqueCube.java index 984f5f2..4f31089 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderOpaqueCube.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderOpaqueCube.java @@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace; public class BlockRenderOpaqueCube extends BlockRenderTexturedCube { public BlockRenderOpaqueCube( - String namespace, String name, + String id, Texture topTexture, Texture bottomTexture, Texture northTexture, Texture southTexture, Texture eastTexture, Texture westTexture ) { super( - namespace, name, + id, topTexture, bottomTexture, northTexture, southTexture, eastTexture, westTexture ); } - public BlockRenderOpaqueCube( - String namespace, String name, - Texture texture - ) { + public BlockRenderOpaqueCube(String id, Texture texture) { this( - namespace, name, + id, texture, texture, texture, texture, texture, texture diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java index 869ff09..dac4099 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java @@ -22,7 +22,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup; import ru.windcorp.progressia.client.graphics.texture.SimpleTexture; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class BlockRenderRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTexturedCube.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTexturedCube.java index 7826652..649ad2f 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTexturedCube.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTexturedCube.java @@ -36,12 +36,12 @@ implements OpaqueCube { private final Map textures = new HashMap<>(); public BlockRenderTexturedCube( - String namespace, String name, + String id, Texture topTexture, Texture bottomTexture, Texture northTexture, Texture southTexture, Texture eastTexture, Texture westTexture ) { - super(namespace, name); + super(id); textures.put(TOP, topTexture); textures.put(BOTTOM, bottomTexture); diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTransparentCube.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTransparentCube.java index e369fdb..a478d2c 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTransparentCube.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderTransparentCube.java @@ -23,25 +23,22 @@ import ru.windcorp.progressia.common.world.block.BlockFace; public class BlockRenderTransparentCube extends BlockRenderTexturedCube { public BlockRenderTransparentCube( - String namespace, String name, + String id, Texture topTexture, Texture bottomTexture, Texture northTexture, Texture southTexture, Texture eastTexture, Texture westTexture ) { super( - namespace, name, + id, topTexture, bottomTexture, northTexture, southTexture, eastTexture, westTexture ); } - public BlockRenderTransparentCube( - String namespace, String name, - Texture texture - ) { + public BlockRenderTransparentCube(String id, Texture texture) { this( - namespace, name, + id, texture, texture, texture, texture, texture, texture diff --git a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerSupplier.java b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerSupplier.java index a074c9f..702282c 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerSupplier.java +++ b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizerSupplier.java @@ -19,21 +19,21 @@ package ru.windcorp.progressia.client.world.cro; import com.google.common.base.Supplier; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public abstract class ChunkRenderOptimizerSupplier extends Namespaced { - public ChunkRenderOptimizerSupplier(String namespace, String name) { - super(namespace, name); + public ChunkRenderOptimizerSupplier(String id) { + super(id); } public abstract ChunkRenderOptimizer createOptimizer(); public static ChunkRenderOptimizerSupplier of( - String namespace, String name, + String id, Supplier supplier ) { - return new ChunkRenderOptimizerSupplier(namespace, name) { + return new ChunkRenderOptimizerSupplier(id) { @Override public ChunkRenderOptimizer createOptimizer() { return supplier.get(); diff --git a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizers.java b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizers.java index 0447675..22b33b2 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizers.java +++ b/src/main/java/ru/windcorp/progressia/client/world/cro/ChunkRenderOptimizers.java @@ -30,7 +30,7 @@ public class ChunkRenderOptimizers { static { register(ChunkRenderOptimizerSupplier.of( - "Default", "OpaqueCube", + "Default:OpaqueCube", ChunkRenderOptimizerCube::new )); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java index 32c9be1..bb53974 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java @@ -1,12 +1,12 @@ package ru.windcorp.progressia.client.world.entity; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.entity.EntityData; public abstract class EntityRender extends Namespaced { - public EntityRender(String namespace, String name) { - super(namespace, name); + public EntityRender(String id) { + super(id); } public abstract EntityRenderable createRenderable(EntityData entity); diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java index e79cebe..f4b8ccf 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -6,7 +6,7 @@ import ru.windcorp.progressia.client.graphics.texture.TextureLoader; import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; import ru.windcorp.progressia.client.graphics.texture.TextureSettings; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class EntityRenderRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java index 13f72a7..65cf3a2 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRender.java @@ -3,13 +3,13 @@ package ru.windcorp.progressia.client.world.tile; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.block.BlockFace; public class TileRender extends Namespaced { - public TileRender(String namespace, String name) { - super(namespace, name); + public TileRender(String id) { + super(id); } public void render(ShapeRenderHelper renderer, BlockFace face) { diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderGrass.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderGrass.java index a7be290..2e932e1 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderGrass.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderGrass.java @@ -18,10 +18,10 @@ public class TileRenderGrass extends TileRender implements OpaqueTile { private final Texture sideTexture; public TileRenderGrass( - String namespace, String name, + String id, Texture top, Texture side ) { - super(namespace, name); + super(id); this.topTexture = top; this.sideTexture = side; } diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java index 889ec5a..8f63ed7 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java @@ -22,7 +22,7 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup; import ru.windcorp.progressia.client.graphics.texture.SimpleTexture; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class TileRenderRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderSimple.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderSimple.java index 32e4435..0e4f3b6 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderSimple.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderSimple.java @@ -16,8 +16,8 @@ public class TileRenderSimple extends TileRender implements OpaqueTile { private final Texture texture; - public TileRenderSimple(String namespace, String name, Texture texture) { - super(namespace, name); + public TileRenderSimple(String id, Texture texture) { + super(id); this.texture = texture; } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java index 065bbdf..0f27553 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java @@ -1,11 +1,11 @@ package ru.windcorp.progressia.common.comms.controls; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public class ControlData extends Namespaced { - public ControlData(String namespace, String name) { - super(namespace, name); + public ControlData(String id) { + super(id); } } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java index 8c9146a..4d817a5 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.common.comms.controls; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class ControlDataRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java index 20fbd24..e9b37d3 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java @@ -6,8 +6,8 @@ public class PacketControl extends Packet { private final ControlData control; - public PacketControl(String namespace, String name, ControlData control) { - super(namespace, name); + public PacketControl(String id, ControlData control) { + super(id); this.control = control; } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java index 114aa18..425a64b 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java @@ -1,11 +1,11 @@ package ru.windcorp.progressia.common.comms.packets; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public class Packet extends Namespaced { - public Packet(String namespace, String name) { - super(namespace, name); + public Packet(String id) { + super(id); } } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java index 4e12d23..c45ef9b 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java @@ -5,7 +5,11 @@ public class PacketSetLocalPlayer extends Packet { private long localPlayerEntityId; public PacketSetLocalPlayer(long entityId) { - super("Core", "SetLocalPlayer"); + this("Core:SetLocalPlayer", entityId); + } + + protected PacketSetLocalPlayer(String id, long entityId) { + super(id); this.localPlayerEntityId = entityId; } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java index 32bd02c..929902a 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java @@ -4,8 +4,8 @@ import ru.windcorp.progressia.common.world.WorldData; public abstract class PacketWorldChange extends Packet { - public PacketWorldChange(String namespace, String name) { - super(namespace, name); + public PacketWorldChange(String id) { + super(id); } public abstract void apply(WorldData world); diff --git a/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java index 14fa24c..767c1f1 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/InspectingStatefulObjectLayout.java @@ -43,10 +43,8 @@ extends AbstractStatefulObjectLayout { } @Override - public StateFieldBuilder getBuilder(String namespace, String name) { - return new InspectingStateFieldBuilder( - namespace, name - ); + public StateFieldBuilder getBuilder(String id) { + return new InspectingStateFieldBuilder(id); } private class InspectingStateFieldBuilder implements StateFieldBuilder { @@ -56,7 +54,7 @@ extends AbstractStatefulObjectLayout { @Override public IntStateField build() { return registerField(new IntStateField( - namespace, name, + id, isLocal, fieldIndexCounters.getIntsThenIncrement() )); @@ -64,16 +62,12 @@ extends AbstractStatefulObjectLayout { } - private final String namespace; - private final String name; + private final String id; private boolean isLocal = true; - public InspectingStateFieldBuilder( - String namespace, String name - ) { - this.namespace = namespace; - this.name = name; + public InspectingStateFieldBuilder(String id) { + this.id = id; } @Override diff --git a/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java b/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java index 433ccd5..3ce0165 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java +++ b/src/main/java/ru/windcorp/progressia/common/state/IntStateField.java @@ -7,11 +7,11 @@ import java.io.IOException; public class IntStateField extends StateField { public IntStateField( - String namespace, String name, + String id, boolean isLocal, int index ) { - super(namespace, name, isLocal, index); + super(id, isLocal, index); } public int get(StatefulObject object) { diff --git a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java index 95e7593..05abaec 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/OptimizedStatefulObjectLayout.java @@ -35,7 +35,7 @@ extends AbstractStatefulObjectLayout { } @Override - public StateFieldBuilder getBuilder(String namespace, String name) { + public StateFieldBuilder getBuilder(String id) { return new RetrieverStateFieldBuilder(); } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StateField.java b/src/main/java/ru/windcorp/progressia/common/state/StateField.java index 4238a8b..837f661 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StateField.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StateField.java @@ -4,7 +4,7 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public abstract class StateField extends Namespaced { @@ -12,11 +12,11 @@ public abstract class StateField extends Namespaced { private final int index; public StateField( - String namespace, String name, + String id, boolean isLocal, int index ) { - super(namespace, name); + super(id); this.isLocal = isLocal; this.index = index; } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java index f35bf19..65bac14 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java @@ -5,7 +5,7 @@ import java.io.DataOutput; import java.io.IOException; import java.util.Objects; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; /** * An abstract class describing objects that have trackable state, @@ -44,10 +44,9 @@ public abstract class StatefulObject extends Namespaced { public StatefulObject( StatefulObjectRegistry type, - String namespace, - String name + String id ) { - super(namespace, name); + super(id); this.layout = type.getLayout(getId()); this.storage = getLayout().createStorage(); } @@ -96,8 +95,8 @@ public abstract class StatefulObject extends Namespaced { * * @return a configured builder */ - protected StateFieldBuilder field(String namespace, String name) { - StateFieldBuilder builder = getLayout().getBuilder(namespace, name); + protected StateFieldBuilder field(String id) { + StateFieldBuilder builder = getLayout().getBuilder(id); builder.setOrdinal(fieldOrdinal); fieldOrdinal++; diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectLayout.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectLayout.java index 5c36487..5165662 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectLayout.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectLayout.java @@ -43,6 +43,6 @@ public abstract class StatefulObjectLayout { public abstract int computeHashCode(StatefulObject object); public abstract boolean areEqual(StatefulObject a, StatefulObject b); - public abstract StateFieldBuilder getBuilder(String namespace, String name); + public abstract StateFieldBuilder getBuilder(String id); } diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java index 1c118ab..52446cc 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java @@ -5,8 +5,8 @@ import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import ru.windcorp.progressia.common.util.Namespaced; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; /** * Registry-like object for identification of various {@link StatefulObject} @@ -30,8 +30,8 @@ public class StatefulObjectRegistry { private final AtomicBoolean isRegistered = new AtomicBoolean(false); - public Type(String namespace, String name, Factory factory) { - super(namespace, name); + public Type(String id, Factory factory) { + super(id); this.factory = factory; } @@ -91,8 +91,8 @@ public class StatefulObjectRegistry { return registry.get(id).build(); } - public void register(String namespace, String name, Factory factory) { - registry.register(new Type<>(namespace, name, factory)); + public void register(String id, Factory factory) { + registry.register(new Type<>(id, factory)); } } diff --git a/src/main/java/ru/windcorp/progressia/common/util/NamespacedUtil.java b/src/main/java/ru/windcorp/progressia/common/util/NamespacedUtil.java deleted file mode 100644 index 2e5dafd..0000000 --- a/src/main/java/ru/windcorp/progressia/common/util/NamespacedUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -package ru.windcorp.progressia.common.util; - -import java.util.Objects; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -public class NamespacedUtil { - - public static final char SEPARATOR = ':'; - public static final int MAX_PART_LENGTH = 127; - public static final int MAX_TOTAL_LENGTH = MAX_PART_LENGTH * 2 + 1; - - private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$"; - - private static final Predicate PART_CHECKER = - Pattern.compile(PART_REGEX).asPredicate(); - - public static String getId(String namespace, String name) { - checkPart(namespace, "Namespace"); - checkPart(name, "Name"); - - return namespace + SEPARATOR + name; - } - - private static void checkPart(String data, String name) { - Objects.requireNonNull(data, name); - - if (data.length() > MAX_PART_LENGTH) { - throw new IllegalArgumentException( - name + " \"" + data + "\" is too long. " - + "Expected at most " + MAX_PART_LENGTH - + " characters" - ); - } - - if (!PART_CHECKER.test(name)) { - throw new IllegalArgumentException( - name + " \"" + data + "\" is invalid. " - + "Allowed is: " + PART_REGEX - ); - } - } - - private NamespacedUtil() {} - -} diff --git a/src/main/java/ru/windcorp/progressia/common/util/namespaces/IllegalIdException.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/IllegalIdException.java new file mode 100644 index 0000000..92fbec0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/IllegalIdException.java @@ -0,0 +1,27 @@ +package ru.windcorp.progressia.common.util.namespaces; + +public class IllegalIdException extends RuntimeException { + + private static final long serialVersionUID = -1572240191058305981L; + + public IllegalIdException() { + super(); + } + + protected IllegalIdException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public IllegalIdException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalIdException(String message) { + super(message); + } + + public IllegalIdException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/Namespaced.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/Namespaced.java similarity index 51% rename from src/main/java/ru/windcorp/progressia/common/util/Namespaced.java rename to src/main/java/ru/windcorp/progressia/common/util/namespaces/Namespaced.java index 635ea85..33b9f74 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/Namespaced.java +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/Namespaced.java @@ -15,50 +15,27 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *******************************************************************************/ -package ru.windcorp.progressia.common.util; +package ru.windcorp.progressia.common.util.namespaces; -import java.util.Objects; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -public abstract class Namespaced extends Named { +public abstract class Namespaced { - private static final char SEPARATOR = ':'; - - private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$"; - - private static final Predicate PART_CHECKER = - Pattern.compile(PART_REGEX).asPredicate(); - - private final String namespace; private final String id; - public Namespaced(String namespace, String name) { - super(name); - this.namespace = Objects.requireNonNull(namespace, "namespace"); - this.id = namespace + SEPARATOR + name; - - if (!PART_CHECKER.test(name)) { - throw new IllegalArgumentException( - "Name \"" + name + "\" is invalid. " - + "Allowed is: " + PART_REGEX - ); - } - - if (!PART_CHECKER.test(namespace)) { - throw new IllegalArgumentException( - "Namespace \"" + namespace + "\" is invalid. " - + "Allowed is: " + PART_REGEX - ); - } + public Namespaced(String id) { + NamespacedUtil.checkId(id); + this.id = id; } - public String getId() { + public final String getId() { return id; } public String getNamespace() { - return namespace; + return NamespacedUtil.getNamespace(getId()); + } + + public String getName() { + return NamespacedUtil.getName(getId()); } @Override @@ -75,15 +52,10 @@ public abstract class Namespaced extends Named { public boolean equals(Object obj) { if (this == obj) return true; - if (!super.equals(obj)) - return false; if (getClass() != obj.getClass()) return false; Namespaced other = (Namespaced) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) + if (!id.equals(other.id)) return false; return true; } diff --git a/src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java similarity index 92% rename from src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java rename to src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java index a2ce835..7db2f41 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java @@ -1,4 +1,4 @@ -package ru.windcorp.progressia.common.util; +package ru.windcorp.progressia.common.util.namespaces; import java.util.Collection; import java.util.Collections; diff --git a/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedUtil.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedUtil.java new file mode 100644 index 0000000..5ced799 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedUtil.java @@ -0,0 +1,106 @@ +package ru.windcorp.progressia.common.util.namespaces; + +import java.util.Objects; + +import ru.windcorp.jputil.chars.StringUtil; + +public class NamespacedUtil { + + public static final char SEPARATOR = ':'; + + public static final int MAX_ID_LENGTH = 255; + private static final int MAX_PART_LENGTH = (MAX_ID_LENGTH - 1) / 2; + + public static final int MAX_NAMESPACE_LENGTH = MAX_PART_LENGTH; + public static final int MAX_NAME_LENGTH = MAX_PART_LENGTH; + + private static final int MIN_PART_LENGTH = 3; + + public static final int MIN_NAMESPACE_LENGTH = MIN_PART_LENGTH; + public static final int MIN_NAME_LENGTH = MIN_PART_LENGTH; + + /* + * This is the definition of the accepted pattern, but the value of + * these constants is not actually consulted in the check* methods. + */ + private static final String PART_CORE_REGEX = "[A-Z][a-zA-Z0-9]{2," + (MAX_PART_LENGTH - 1) + "}"; + private static final String PART_REGEX = "^" + PART_CORE_REGEX + "$"; + + public static final String NAMESPACE_REGEX = PART_REGEX; + public static final String NAME_REGEX = PART_REGEX; + public static final String ID_REGEX = "^" + PART_CORE_REGEX + ":" + PART_CORE_REGEX + "$"; + + public static String getName(String id) { + checkId(id); + return id.substring(id.indexOf(':') + 1); + } + + public static String getNamespace(String id) { + checkId(id); + return id.substring(0, id.indexOf(':')); + } + + public static String getId(String namespace, String name) { + checkPart(namespace, 0, namespace.length(), "Namespace"); + checkPart(name, 0, name.length(), "Name"); + + return namespace + SEPARATOR + name; + } + + public static void checkId(String id) { + Objects.requireNonNull(id, "id"); + + int firstSeparator = id.indexOf(SEPARATOR); + + boolean areSeparatorsInvalid = (firstSeparator < 0) || (id.indexOf(SEPARATOR, firstSeparator + 1) >= 0); + + if (areSeparatorsInvalid) { + int separators = StringUtil.count(id, SEPARATOR); + throw new IllegalIdException( + "ID \"" + id + "\" is invalid. " + + (separators == 0 ? "No " : "Too many (" + separators + ") ") + + "separators '" + SEPARATOR + "' found, exactly one required" + ); + } + + checkPart(id, 0, firstSeparator, "namespace"); + checkPart(id, firstSeparator + 1, id.length() - firstSeparator - 1, "name"); + } + + private static void checkPart(String data, int offset, int length, String nameForErrors) { + Objects.requireNonNull(data, nameForErrors); + + if (length > MAX_PART_LENGTH) { + throw new IllegalIdException( + nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too long. " + + "Expected at most " + MAX_PART_LENGTH + + " characters" + ); + } else if (length < MIN_PART_LENGTH) { + throw new IllegalIdException( + nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is too short. " + + "Expected at lest " + MIN_PART_LENGTH + + " characters" + ); + } + + // Don't actually use *_REGEX for speed + + for (int i = 0; i < length; ++i) { + char c = data.charAt(i + offset); + if (!( + ( c >= 'A' && c <= 'Z') || + (i != 0 && c >= 'a' && c <= 'z') || + (i != 0 && c >= '0' && c <= '9') + )) { + throw new IllegalIdException( + nameForErrors + " \"" + data.substring(offset, offset + length) + "\" is invalid. " + + "Allowed is: " + PART_REGEX + ); + } + } + } + + private NamespacedUtil() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java index e1ea62d..87feb5e 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockData.java @@ -19,12 +19,12 @@ package ru.windcorp.progressia.common.world.block; import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.CollisionModel; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public class BlockData extends Namespaced { - public BlockData(String namespace, String name) { - super(namespace, name); + public BlockData(String id) { + super(id); } public CollisionModel getCollisionModel() { diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java index 6711b3a..51c562b 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java @@ -17,7 +17,7 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.block; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class BlockDataRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index 5cd79fd..b8fa18f 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -20,8 +20,8 @@ public class EntityData extends StatefulObject implements Collideable { private double age = 0; - public EntityData(String namespace, String name) { - super(EntityDataRegistry.getInstance(), namespace, name); + public EntityData(String id) { + super(EntityDataRegistry.getInstance(), id); } public Vec3 getPosition() { diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java index 5624b71..9f0c8f1 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java @@ -10,8 +10,8 @@ public class EntityDataRegistry extends StatefulObjectRegistry { return INSTANCE; } - public void register(String namespace, String name) { - super.register(namespace, name, () -> new EntityData(namespace, name)); + public void register(String id) { + super.register(id, () -> new EntityData(id)); } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java index f6c5c8b..68933bd 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/PacketEntityChange.java @@ -15,7 +15,11 @@ public class PacketEntityChange extends PacketWorldChange { private final DataBuffer buffer = new DataBuffer(); public PacketEntityChange() { - super("Core", "EntityChange"); + super("Core:EntityChange"); + } + + protected PacketEntityChange(String id) { + super(id); } public long getEntityId() { diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java index 7cbbded..d3354f6 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileData.java @@ -17,12 +17,12 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.tile; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public class TileData extends Namespaced { - public TileData(String namespace, String name) { - super(namespace, name); + public TileData(String id) { + super(id); } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java index c19fb3d..7ca3168 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java @@ -17,7 +17,7 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.tile; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class TileDataRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java index 17ff7f5..ae708a2 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java @@ -1,14 +1,14 @@ package ru.windcorp.progressia.server.comms.controls; import ru.windcorp.progressia.common.comms.controls.PacketControl; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.comms.Client; public abstract class ControlLogic extends Namespaced { - public ControlLogic(String namespace, String name) { - super(namespace, name); + public ControlLogic(String id) { + super(id); } public abstract void apply( diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java index d37f328..94f4458 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.server.comms.controls; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class ControlLogicRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java index 70fada1..f823dda 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java @@ -35,7 +35,11 @@ public class ImplementedChangeTracker implements Changer { private BlockData block; public SetBlock() { - super("Core", "SetBlock"); + this("Core:SetBlock"); + } + + protected SetBlock(String id) { + super(id); } public void initialize(Vec3i position, BlockData block) { @@ -76,7 +80,11 @@ public class ImplementedChangeTracker implements Changer { private boolean shouldAdd; public AddOrRemoveTile() { - super("Core", "AddOrRemoveTile"); + this("Core:AddOrRemoveTile"); + } + + protected AddOrRemoveTile(String id) { + super(id); } public void initialize( diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java index a010749..5c9645a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogic.java @@ -1,11 +1,11 @@ package ru.windcorp.progressia.server.world.block; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; public class BlockLogic extends Namespaced { - public BlockLogic(String namespace, String name) { - super(namespace, name); + public BlockLogic(String id) { + super(id); } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java index 3666c5d..aabf7fd 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.server.world.block; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class BlockLogicRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java index 22f31c5..f678e25 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java @@ -1,14 +1,14 @@ package ru.windcorp.progressia.server.world.entity; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.world.Changer; import ru.windcorp.progressia.server.world.TickContext; public class EntityLogic extends Namespaced { - public EntityLogic(String namespace, String name) { - super(namespace, name); + public EntityLogic(String id) { + super(id); } public void tick(EntityData entity, TickContext context, Changer changer) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java index 21e37f9..1517c8f 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.server.world.entity; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class EntityLogicRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java index f530c26..c44ec7e 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java @@ -1,12 +1,12 @@ package ru.windcorp.progressia.server.world.tile; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.world.block.BlockFace; public class TileLogic extends Namespaced { - public TileLogic(String namespace, String name) { - super(namespace, name); + public TileLogic(String id) { + super(id); } public boolean canOccupyFace(TileTickContext context) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java index a5b522b..b997702 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java @@ -1,6 +1,6 @@ package ru.windcorp.progressia.server.world.tile; -import ru.windcorp.progressia.common.util.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; public class TileLogicRegistry extends NamespacedRegistry { diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 3074815..ffe6d1b 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -42,65 +42,65 @@ public class TestContent { } private static void registerBlocks() { - register(new BlockData("Test", "Air") { + register(new BlockData("Test:Air") { @Override public CollisionModel getCollisionModel() { return null; } }); - register(new BlockRenderNone("Test", "Air")); - register(new BlockLogic("Test", "Air")); + register(new BlockRenderNone("Test:Air")); + register(new BlockLogic("Test:Air")); - register(new BlockData("Test", "Dirt")); - register(new BlockRenderOpaqueCube("Test", "Dirt", getBlockTexture("dirt"))); - register(new BlockLogic("Test", "Dirt")); + register(new BlockData("Test:Dirt")); + register(new BlockRenderOpaqueCube("Test:Dirt", getBlockTexture("dirt"))); + register(new BlockLogic("Test:Dirt")); - register(new BlockData("Test", "Stone")); - register(new BlockRenderOpaqueCube("Test", "Stone", getBlockTexture("stone"))); - register(new BlockLogic("Test", "Stone")); + register(new BlockData("Test:Stone")); + register(new BlockRenderOpaqueCube("Test:Stone", getBlockTexture("stone"))); + register(new BlockLogic("Test:Stone")); - register(new BlockData("Test", "Compass")); - register(new BlockRenderOpaqueCube("Test", "Compass", getBlockTexture("compass"))); - register(new BlockLogic("Test", "Compass")); + register(new BlockData("Test:Compass")); + register(new BlockRenderOpaqueCube("Test:Compass", getBlockTexture("compass"))); + register(new BlockLogic("Test:Compass")); - register(new BlockData("Test", "Glass")); - register(new BlockRenderTransparentCube("Test", "Glass", getBlockTexture("glass_clear"))); - register(new BlockLogic("Test", "Glass")); + register(new BlockData("Test:Glass")); + register(new BlockRenderTransparentCube("Test:Glass", getBlockTexture("glass_clear"))); + register(new BlockLogic("Test:Glass")); } private static void registerTiles() { - register(new TileData("Test", "Grass")); - register(new TileRenderGrass("Test", "Grass", getTileTexture("grass_top"), getTileTexture("grass_side"))); - register(new TileLogic("Test", "Grass")); + register(new TileData("Test:Grass")); + register(new TileRenderGrass("Test:Grass", getTileTexture("grass_top"), getTileTexture("grass_side"))); + register(new TileLogic("Test:Grass")); - register(new TileData("Test", "Stones")); - register(new TileRenderSimple("Test", "Stones", getTileTexture("stones"))); - register(new TileLogic("Test", "Stones")); + register(new TileData("Test:Stones")); + register(new TileRenderSimple("Test:Stones", getTileTexture("stones"))); + register(new TileLogic("Test:Stones")); - register(new TileData("Test", "YellowFlowers")); - register(new TileRenderSimple("Test", "YellowFlowers", getTileTexture("yellow_flowers"))); - register(new TileLogic("Test", "YellowFlowers")); + register(new TileData("Test:YellowFlowers")); + register(new TileRenderSimple("Test:YellowFlowers", getTileTexture("yellow_flowers"))); + register(new TileLogic("Test:YellowFlowers")); - register(new TileData("Test", "Sand")); - register(new TileRenderSimple("Test", "Sand", getTileTexture("sand"))); - register(new TileLogic("Test", "Sand")); + register(new TileData("Test:Sand")); + register(new TileRenderSimple("Test:Sand", getTileTexture("sand"))); + register(new TileLogic("Test:Sand")); } private static void registerEntities() { float scale = 1.8f / 8; - registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); - register(new TestEntityRenderHuman()); - register(new EntityLogic("Test", "Player")); + registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); + register(new TestEntityRenderHuman("Test:Player")); + register(new EntityLogic("Test:Player")); - register("Test", "Statie", TestEntityDataStatie::new); - register(new TestEntityRenderStatie()); - register(new TestEntityLogicStatie()); + register("Test:Statie", TestEntityDataStatie::new); + register(new TestEntityRenderStatie("Test:Statie")); + register(new TestEntityLogicStatie("Test:Statie")); } private static void regsiterControls() { - ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000")); - ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches)); - ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") { + ControlDataRegistry.getInstance().register(new ControlData("Test:Switch000")); + ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test:Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches)); + ControlLogicRegistry.getInstance().register(new ControlLogic("Test:Switch000") { @Override public void apply(Server server, PacketControl packet, Client client) { Vec3i z000 = new Vec3i(0, 0, 0); @@ -111,32 +111,12 @@ public class TestContent { if (data.getBlock(z000).getId().equals("Test:Stone")) { block = BlockDataRegistry.getInstance().get("Test:Glass"); } else { - block = BlockDataRegistry.getInstance().get("Test:Stone"); + block = BlockDataRegistry.getInstance().get("Test:Stone"); } server.getAdHocChanger().setBlock(z000, block); } }); - -// ControlDataRegistry.getInstance().register(new ControlData("Test", "BreakBlock")); -// ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "BreakBlock", new KeyMatcher(GLFW.GLFW_KEY_ENTER, new int[0], 0)::matches)); -// ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "BreakBlock") { -// @Override -// public void apply(Server server, PacketControl packet, Client client) { -// Vec3i z000 = new Vec3i(0, 0, 0); -// -// ChunkData data = server.getWorld().getChunk(z000).getData(); -// -// BlockData block; -// if (data.getBlock(z000).getId().equals("Test:Stone")) { -// block = BlockDataRegistry.getInstance().get("Test:Glass"); -// } else { -// block = BlockDataRegistry.getInstance().get("Test:Stone"); -// } -// -// server.getAdHocChanger().setBlock(z000, block); -// } -// }); } private static void register(BlockData x) { @@ -148,20 +128,20 @@ public class TestContent { } private static void register( - String namespace, String name, + String id, Factory factory ) { - EntityDataRegistry.getInstance().register(namespace, name, factory); + EntityDataRegistry.getInstance().register(id, factory); } private static void registerEntityData( - String namespace, String name, + String id, Consumer transform ) { - EntityDataRegistry.getInstance().register(namespace, name, new Factory() { + EntityDataRegistry.getInstance().register(id, new Factory() { @Override public EntityData build() { - EntityData entity = new EntityData(namespace, name); + EntityData entity = new EntityData(id); transform.accept(entity); return entity; } diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java index 19d47c9..8313c01 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityDataStatie.java @@ -7,10 +7,14 @@ import ru.windcorp.progressia.common.world.entity.EntityData; public class TestEntityDataStatie extends EntityData { private final IntStateField size = - field("Test", "Size").setShared().ofInt().build(); + field("Test:Size").setShared().ofInt().build(); public TestEntityDataStatie() { - super("Test", "Statie"); + this("Test:Statie"); + } + + protected TestEntityDataStatie(String id) { + super(id); setCollisionModel(new AABB(0, 0, 0, 1, 1, 1)); setSizeNow(16); } diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityLogicStatie.java b/src/main/java/ru/windcorp/progressia/test/TestEntityLogicStatie.java index 9826938..9918f07 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityLogicStatie.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityLogicStatie.java @@ -7,8 +7,8 @@ import ru.windcorp.progressia.server.world.entity.EntityLogic; public class TestEntityLogicStatie extends EntityLogic { - public TestEntityLogicStatie() { - super("Test", "Statie"); + public TestEntityLogicStatie(String id) { + super(id); } @Override diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java index cecfacf..208c89a 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderHuman.java @@ -9,6 +9,7 @@ import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; import ru.windcorp.progressia.client.graphics.model.StaticModel; import ru.windcorp.progressia.client.graphics.texture.ComplexTexture; +import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.world.entity.HumanoidModel; import ru.windcorp.progressia.client.world.entity.EntityRender; @@ -29,12 +30,16 @@ public class TestEntityRenderHuman extends EntityRender { private final Renderable rightArm; private final Renderable leftLeg; private final Renderable rightLeg; + + private final TexturePrimitive skin; - public TestEntityRenderHuman() { - super("Test", "Player"); + public TestEntityRenderHuman(String id) { + super(id); + + this.skin = fetchSkin(); ComplexTexture texture = new ComplexTexture( - EntityRenderRegistry.getEntityTexture("pyotr"), + this.skin, 16, 16 ); @@ -46,6 +51,14 @@ public class TestEntityRenderHuman extends EntityRender { this.leftLeg = createLimb(texture, 4, 0, 0, 0, false, true); this.rightLeg = createLimb(texture, 0, 8, 0, 4, false, false); } + + protected TexturePrimitive fetchSkin() { + return EntityRenderRegistry.getEntityTexture("pyotr"); + } + + public TexturePrimitive getSkin() { + return skin; + } private Renderable createBody(ComplexTexture texture) { return createLayeredCuboid( diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderJavapony.java b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderJavapony.java index 61c15f3..ca6f53b 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderJavapony.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderJavapony.java @@ -32,8 +32,8 @@ public class TestEntityRenderJavapony extends EntityRender { private final Renderable rightForeLeg; private final Renderable rightHindLeg; - public TestEntityRenderJavapony() { - super("Test", "Javapony"); + public TestEntityRenderJavapony(String id) { + super(id); ComplexTexture texture = new ComplexTexture( EntityRenderRegistry.getEntityTexture("javapony"), diff --git a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderStatie.java b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderStatie.java index ce388f8..e7b2b81 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestEntityRenderStatie.java +++ b/src/main/java/ru/windcorp/progressia/test/TestEntityRenderStatie.java @@ -19,8 +19,8 @@ public class TestEntityRenderStatie extends EntityRender { .setColorMultiplier(1, 1, 0) .create(); - public TestEntityRenderStatie() { - super("Test", "Statie"); + public TestEntityRenderStatie(String id) { + super(id); } @Override diff --git a/src/test/java/ru/windcorp/jputil/chars/stringUtil/SplitAtTest.java b/src/test/java/ru/windcorp/jputil/chars/stringUtil/SplitAtTest.java new file mode 100644 index 0000000..de5a6ae --- /dev/null +++ b/src/test/java/ru/windcorp/jputil/chars/stringUtil/SplitAtTest.java @@ -0,0 +1,70 @@ +package ru.windcorp.jputil.chars.stringUtil; + +import static org.junit.Assert.assertArrayEquals; + +import java.util.Random; + +import org.junit.Test; + +import ru.windcorp.jputil.chars.StringUtil; + +public class SplitAtTest { + + @Test + public void testExamplesFromDocs() { + test("a.b.c", new int[] {1, 3}, new String[] {"a", "b", "c"}); + test("a..b", new int[] {1, 2}, new String[] {"a", "", "b"}); + test(".b.", new int[] {0, 2}, new String[] {"", "b", ""}); + test("a.b", new int[] {1, 1, 1}, new String[] {"a", "", "", "b"}); + } + + @Test + public void testIndexPermutations() { + Random random = new Random(0); + + int stringLength = 1000; + char[] chars = new char[stringLength]; + + for (int i = 0; i < stringLength; ++i) { + chars[i] = (char) ('a' + random.nextInt('z' - 'a')); + } + + String src = new String(chars); + + int[] indices = new int[100]; + + for (int i = 0; i < indices.length; ++i) { + indices[i] = random.nextInt(stringLength); + } + + String[] expected = StringUtil.splitAt(src, indices); + + for (int i = 0; i < 10000; ++i) { + shuffleArray(indices, random); + + int[] copy = indices.clone(); + test(src, indices, expected); + assertArrayEquals(indices, copy); // Make sure indices array hasn't changed + } + } + + // Shamelessly copied from + // https://stackoverflow.com/a/1520212/4463352 + // Thanks, https://stackoverflow.com/users/15459/philho! + + // Implementing Fisher–Yates shuffle + private static void shuffleArray(int[] ar, Random random) { + for (int i = ar.length - 1; i > 0; i--) { + int index = random.nextInt(i + 1); + // Simple swap + int a = ar[index]; + ar[index] = ar[i]; + ar[i] = a; + } + } + + private void test(String string, int[] at, String[] expecteds) { + assertArrayEquals(expecteds, StringUtil.splitAt(string, at)); + } + +} diff --git a/src/test/java/ru/windcorp/progressia/util/NamespacedTest.java b/src/test/java/ru/windcorp/progressia/util/NamespacedTest.java index fb226b0..47dc76a 100644 --- a/src/test/java/ru/windcorp/progressia/util/NamespacedTest.java +++ b/src/test/java/ru/windcorp/progressia/util/NamespacedTest.java @@ -29,38 +29,40 @@ import java.util.Random; import org.junit.Test; import junit.framework.AssertionFailedError; -import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.IllegalIdException; +import ru.windcorp.progressia.common.util.namespaces.Namespaced; +import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil; public class NamespacedTest { class TestNamespaced extends Namespaced { - public TestNamespaced(String namespace, String name) { - super(namespace, name); + public TestNamespaced(String id) { + super(id); } } void shouldReject(String a, String b) { try { - new TestNamespaced(a, b); - } catch (IllegalArgumentException | NullPointerException e) { + new TestNamespaced(NamespacedUtil.getId(a, b)); + } catch (IllegalIdException | NullPointerException e) { try { - new TestNamespaced(b, a); - } catch (IllegalArgumentException | NullPointerException e1) { + new TestNamespaced(NamespacedUtil.getId(b, a)); + } catch (IllegalIdException | NullPointerException e1) { return; } } - throw new AssertionFailedError("Expected NPE or IAE for: \"" + a + "\":\"" + b + "\""); + throw new AssertionFailedError("Expected NPE or IllegalIdException for: \"" + a + "\":\"" + b + "\""); } @Test public void shouldAllow() { - new TestNamespaced("Something", "Usual"); - new TestNamespaced("Vry", "Sml"); - new TestNamespaced("ALL", "CAPS"); - new TestNamespaced("WithDigits12345", "MoreDigits67890"); + new TestNamespaced("Something:Usual"); + new TestNamespaced("Vry:Sml"); + new TestNamespaced("ALL:CAPS"); + new TestNamespaced("WithDigits12345:MoreDigits67890"); } @Test @@ -80,17 +82,17 @@ public class NamespacedTest { shouldReject("XS", "Normal"); shouldReject("", "Normal"); shouldReject("Contains:separators", "Normal"); - shouldReject("СодержитРусский", "Normal"); + shouldReject("СодержитНеАльфанум", "Normal"); } @Test public void shouldRejectGarbage() { Random random = new Random(0); - byte[] bytes = new byte[1024]; + byte[] bytes = new byte[NamespacedUtil.MAX_NAME_LENGTH]; for (int attempt = 0; attempt < 10000; ++attempt) { random.nextBytes(bytes); - bytes[0] = 'a'; // Make sure it is invalid + bytes[bytes.length - 1] = '!'; // Make sure it is invalid shouldReject(new String(bytes), "ContainsUtterGarbage"); } } @@ -108,8 +110,8 @@ public class NamespacedTest { String namespace = getRandomValidString(random); String name = getRandomValidString(random); - TestNamespaced a = new TestNamespaced(namespace, name); - TestNamespaced b = new TestNamespaced(namespace, name); + TestNamespaced a = new TestNamespaced(NamespacedUtil.getId(namespace, name)); + TestNamespaced b = new TestNamespaced(NamespacedUtil.getId(namespace, name)); contains.add(a); hashSet.add(b); @@ -119,7 +121,7 @@ public class NamespacedTest { String namespace = getRandomValidString(random); String name = getRandomValidString(random); - TestNamespaced c = new TestNamespaced(namespace, name); + TestNamespaced c = new TestNamespaced(NamespacedUtil.getId(namespace, name)); doesNotContain.add(c); } @@ -128,7 +130,7 @@ public class NamespacedTest { Iterator it = doesNotContain.iterator(); while (it.hasNext()) { TestNamespaced next = it.next(); - if (next.getName().equals(x.getName()) && next.getNamespace().equals(x.getNamespace())) { + if (next.getId().equals(x.getId())) { it.remove(); } } @@ -144,7 +146,7 @@ public class NamespacedTest { } String getRandomValidString(Random random) { - char[] chars = new char[random.nextInt(100) + 3]; + char[] chars = new char[random.nextInt(NamespacedUtil.MAX_NAME_LENGTH - 3) + 3]; for (int i = 0; i < chars.length; ++i) { switch (random.nextInt(3)) { From 56eaec522f6d7d23814da3f8aa77f9e7bad1ab1d Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 17 Nov 2020 16:49:31 +0300 Subject: [PATCH 17/24] Renamed NamespacedRegistry and added NamespacedFactoryRegistry - NamespacedRegistry moved to NamespacedInstanceRegistry - Added NamespacedFactoryRegistry --- .../controls/ControlTriggerRegistry.java | 4 +- .../world/block/BlockRenderRegistry.java | 4 +- .../world/entity/EntityRenderRegistry.java | 4 +- .../client/world/tile/TileRenderRegistry.java | 4 +- .../comms/controls/ControlDataRegistry.java | 4 +- .../common/state/StatefulObjectRegistry.java | 6 +- .../namespaces/NamespacedFactoryRegistry.java | 114 ++++++++++++++++++ ...y.java => NamespacedInstanceRegistry.java} | 11 +- .../common/world/block/BlockDataRegistry.java | 4 +- .../common/world/tile/TileDataRegistry.java | 4 +- .../comms/controls/ControlLogicRegistry.java | 4 +- .../world/block/BlockLogicRegistry.java | 4 +- .../world/entity/EntityLogicRegistry.java | 4 +- .../server/world/tile/TileLogicRegistry.java | 4 +- 14 files changed, 146 insertions(+), 29 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedFactoryRegistry.java rename src/main/java/ru/windcorp/progressia/common/util/namespaces/{NamespacedRegistry.java => NamespacedInstanceRegistry.java} (79%) diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java index d0c01c8..65f5d43 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.client.comms.controls; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class ControlTriggerRegistry extends NamespacedRegistry { +public class ControlTriggerRegistry extends NamespacedInstanceRegistry { private static final ControlTriggerRegistry INSTANCE = new ControlTriggerRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java index dac4099..7392986 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/block/BlockRenderRegistry.java @@ -22,9 +22,9 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup; import ru.windcorp.progressia.client.graphics.texture.SimpleTexture; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class BlockRenderRegistry extends NamespacedRegistry { +public class BlockRenderRegistry extends NamespacedInstanceRegistry { private static final BlockRenderRegistry INSTANCE = new BlockRenderRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java index f4b8ccf..1737616 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -6,9 +6,9 @@ import ru.windcorp.progressia.client.graphics.texture.TextureLoader; import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; import ru.windcorp.progressia.client.graphics.texture.TextureSettings; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class EntityRenderRegistry extends NamespacedRegistry { +public class EntityRenderRegistry extends NamespacedInstanceRegistry { private static final EntityRenderRegistry INSTANCE = new EntityRenderRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java index 8f63ed7..4c11c01 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileRenderRegistry.java @@ -22,9 +22,9 @@ import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup; import ru.windcorp.progressia.client.graphics.texture.SimpleTexture; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.resource.ResourceManager; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class TileRenderRegistry extends NamespacedRegistry { +public class TileRenderRegistry extends NamespacedInstanceRegistry { private static final TileRenderRegistry INSTANCE = new TileRenderRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java index 4d817a5..eb787b3 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.common.comms.controls; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class ControlDataRegistry extends NamespacedRegistry { +public class ControlDataRegistry extends NamespacedInstanceRegistry { private static final ControlDataRegistry INSTANCE = new ControlDataRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java index 52446cc..42028cf 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObjectRegistry.java @@ -6,7 +6,7 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicBoolean; import ru.windcorp.progressia.common.util.namespaces.Namespaced; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; /** * Registry-like object for identification of various {@link StatefulObject} @@ -45,8 +45,8 @@ public class StatefulObjectRegistry { } - private final NamespacedRegistry> registry = - new NamespacedRegistry>() { + private final NamespacedInstanceRegistry> registry = + new NamespacedInstanceRegistry>() { @Override public void register(Type element) { super.register(element); diff --git a/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedFactoryRegistry.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedFactoryRegistry.java new file mode 100644 index 0000000..583ccd2 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedFactoryRegistry.java @@ -0,0 +1,114 @@ +package ru.windcorp.progressia.common.util.namespaces; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NamespacedFactoryRegistry +implements Map> { + + @FunctionalInterface + public static interface Factory { + E build(String id); + } + + private final Map> backingMap = + Collections.synchronizedMap(new HashMap<>()); + + private final Logger logger = LogManager.getLogger(getClass()); + + public void register(String id, Factory element) { + if (get(id) != null) { + throw new IllegalArgumentException("ID " + id + " is already registered in " + getClass().getSimpleName()); + } + + logger.debug("Registering {} in {}", id, getClass().getSimpleName()); + backingMap.put(id, element); + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return backingMap.containsKey(key); + } + + public boolean has(String id) { + return backingMap.containsKey(id); + } + + @Override + public boolean containsValue(Object value) { + return backingMap.containsValue(value); + } + + public boolean isRegistered(E element) { + return has(element.getId()); + } + + @Override + public Factory get(Object key) { + return backingMap.get(key); + } + + public E create(String id) { + Factory factory = get(id); + E result = factory.build(id); + if (!result.getId().equals(id)) { + throw new IllegalStateException("Requested ID " + id + " but factory " + factory + " returned an object with ID " + result.getId()); + } + return result; + } + + @Override + public Factory put(String id, Factory factory) { + register(id, factory); + return null; + } + + @Override + public void putAll(Map> m) { + synchronized (backingMap) { + m.entrySet().forEach(e -> register(e.getKey(), e.getValue())); + } + } + + @Override + public Factory remove(Object key) { + return backingMap.remove(key); + } + + @Override + public void clear() { + backingMap.clear(); + } + + @Override + public Set keySet() { + return backingMap.keySet(); + } + + @Override + public Collection> values() { + return backingMap.values(); + } + + @Override + public Set>> entrySet() { + return backingMap.entrySet(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedInstanceRegistry.java similarity index 79% rename from src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java rename to src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedInstanceRegistry.java index 7db2f41..ca4a56f 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/util/namespaces/NamespacedInstanceRegistry.java @@ -7,17 +7,20 @@ import java.util.Map; import java.util.Set; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import com.google.errorprone.annotations.DoNotCall; -public class NamespacedRegistry +public class NamespacedInstanceRegistry implements Map { private final Map backingMap = Collections.synchronizedMap(new HashMap<>()); + private final Logger logger = LogManager.getLogger(getClass()); + public void register(E element) { - LogManager.getLogger(getClass()).debug("Registering " + element.getId()); + logger.debug("Registering {} in {}", element.getId(), getClass().getSimpleName()); backingMap.put(element.getId(), element); } @@ -67,7 +70,7 @@ implements Map { @DoNotCall @Deprecated public E put(String key, E value) { throw new UnsupportedOperationException( - "Use NamespacedRegistry.register(E)" + "Use NamespacedInstanceRegistry.register(E)" ); } @@ -83,7 +86,7 @@ implements Map { @DoNotCall @Deprecated public void putAll(Map m) { throw new UnsupportedOperationException( - "Use NamespacedRegistry.registerAll(Collection)" + "Use NamespacedInstanceRegistry.registerAll(Collection)" ); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java index 51c562b..f3541a3 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockDataRegistry.java @@ -17,9 +17,9 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.block; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class BlockDataRegistry extends NamespacedRegistry { +public class BlockDataRegistry extends NamespacedInstanceRegistry { private static final BlockDataRegistry INSTANCE = new BlockDataRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java index 7ca3168..6a95eae 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/TileDataRegistry.java @@ -17,9 +17,9 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world.tile; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class TileDataRegistry extends NamespacedRegistry { +public class TileDataRegistry extends NamespacedInstanceRegistry { private static final TileDataRegistry INSTANCE = new TileDataRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java index 94f4458..403644d 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.server.comms.controls; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class ControlLogicRegistry extends NamespacedRegistry { +public class ControlLogicRegistry extends NamespacedInstanceRegistry { private static final ControlLogicRegistry INSTANCE = new ControlLogicRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java index aabf7fd..5cdb1f0 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/BlockLogicRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.server.world.block; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class BlockLogicRegistry extends NamespacedRegistry { +public class BlockLogicRegistry extends NamespacedInstanceRegistry { private static final BlockLogicRegistry INSTANCE = new BlockLogicRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java index 1517c8f..012ee09 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.server.world.entity; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class EntityLogicRegistry extends NamespacedRegistry { +public class EntityLogicRegistry extends NamespacedInstanceRegistry { private static final EntityLogicRegistry INSTANCE = new EntityLogicRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java index b997702..2424e46 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.server.world.tile; -import ru.windcorp.progressia.common.util.namespaces.NamespacedRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; -public class TileLogicRegistry extends NamespacedRegistry { +public class TileLogicRegistry extends NamespacedInstanceRegistry { private static final TileLogicRegistry INSTANCE = new TileLogicRegistry(); From cf18da83508345a015d101a0883cfcb294045957 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Tue, 17 Nov 2020 22:53:56 +0300 Subject: [PATCH 18/24] Added block breaking and refactored Controls - Blocks can be broken by pointing at them at pressing LMB - Rewritten ControlTriggers - Rewritten KeyMatcher - CollisionPathComputer now has a 0.5 margin --- .../comms/controls/ControlTriggerLambda.java | 48 +++++ .../controls/ControlTriggerOnKeyPress.java | 40 ---- .../comms/controls/ControlTriggers.java | 176 ++++++++++++++++++ .../client/graphics/input/KeyMatcher.java | 82 ++++---- .../collision/CollisionPathComputer.java | 14 +- .../comms/controls/ControlDataRegistry.java | 4 +- .../server/comms/controls/ControlLogic.java | 18 ++ .../test/ControlBreakBlockData.java | 22 +++ .../windcorp/progressia/test/TestContent.java | 75 +++++--- 9 files changed, 378 insertions(+), 101 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerLambda.java delete mode 100644 src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java create mode 100644 src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggers.java create mode 100644 src/main/java/ru/windcorp/progressia/test/ControlBreakBlockData.java diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerLambda.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerLambda.java new file mode 100644 index 0000000..d2a0d69 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerLambda.java @@ -0,0 +1,48 @@ +package ru.windcorp.progressia.client.comms.controls; + +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.common.comms.controls.ControlData; +import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; +import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil; + +public class ControlTriggerLambda extends ControlTriggerInputBased { + + private final String packetId; + private final Predicate predicate; + private final BiConsumer dataWriter; + + public ControlTriggerLambda( + String id, + Predicate predicate, + BiConsumer dataWriter + ) { + super(id); + + this.packetId = NamespacedUtil.getId( + NamespacedUtil.getNamespace(id), + "ControlKeyPress" + NamespacedUtil.getName(id) + ); + + this.predicate = predicate; + this.dataWriter = dataWriter; + } + + @Override + public PacketControl onInputEvent(InputEvent event) { + if (!predicate.test(event)) return null; + + PacketControl packet = new PacketControl( + packetId, + ControlDataRegistry.getInstance().create(getId()) + ); + + dataWriter.accept(event, packet.getControl()); + + return packet; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java deleted file mode 100644 index 746cb9f..0000000 --- a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java +++ /dev/null @@ -1,40 +0,0 @@ -package ru.windcorp.progressia.client.comms.controls; - -import java.util.function.Predicate; - -import ru.windcorp.progressia.client.graphics.input.InputEvent; -import ru.windcorp.progressia.client.graphics.input.KeyEvent; -import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; -import ru.windcorp.progressia.common.comms.controls.PacketControl; -import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil; - -public class ControlTriggerOnKeyPress extends ControlTriggerInputBased { - - private final Predicate predicate; - private final PacketControl packet; - - public ControlTriggerOnKeyPress( - String id, - Predicate predicate - ) { - super(id); - this.predicate = predicate; - this.packet = new PacketControl( - NamespacedUtil.getId(getNamespace(), "ControlKeyPress" + getName()), - ControlDataRegistry.getInstance().get(getId()) - ); - } - - @Override - public PacketControl onInputEvent(InputEvent event) { - if (!(event instanceof KeyEvent)) return null; - - KeyEvent keyEvent = (KeyEvent) event; - - if (!keyEvent.isPress()) return null; - if (!predicate.test(keyEvent)) return null; - - return packet; - } - -} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggers.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggers.java new file mode 100644 index 0000000..5930ee6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggers.java @@ -0,0 +1,176 @@ +package ru.windcorp.progressia.client.comms.controls; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.common.comms.controls.ControlData; + +public class ControlTriggers { + + public static ControlTriggerInputBased of( + String id, + BiConsumer dataWriter, + Predicate predicate + ) { + return new ControlTriggerLambda(id, predicate, dataWriter); + } + + public static ControlTriggerInputBased of( + String id, + Consumer dataWriter, + Predicate predicate + ) { + return of( + id, + (input, control) -> dataWriter.accept(control), + predicate + ); + } + + public static ControlTriggerInputBased of( + String id, + Predicate predicate + ) { + return of( + id, + (input, control) -> {}, + predicate + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + Class inputType, + BiConsumer dataWriter, + Predicate... predicates + ) { + return of( + id, + createCheckedDataWriter(inputType, dataWriter), + createCheckedCompoundPredicate(inputType, predicates) + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + Class inputType, + Consumer dataWriter, + Predicate... predicates + ) { + return of( + id, + inputType, + (input, control) -> dataWriter.accept(control), + predicates + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + Class inputType, + Predicate... predicates + ) { + return of( + id, + (input, control) -> {}, + createCheckedCompoundPredicate(inputType, predicates) + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + BiConsumer dataWriter, + Predicate... predicates + ) { + return of( + id, + InputEvent.class, + dataWriter, + predicates + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + Consumer dataWriter, + Predicate... predicates + ) { + return of( + id, + (input, control) -> dataWriter.accept(control), + predicates + ); + } + + @SafeVarargs + public static ControlTriggerInputBased of( + String id, + Predicate... predicates + ) { + return of( + id, + InputEvent.class, + (input, control) -> {}, + predicates + ); + } + + private static + + BiConsumer + createCheckedDataWriter( + Class inputType, + BiConsumer dataWriter + ) { + return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control); + } + + private static + + Predicate + createCheckedCompoundPredicate( + Class inputType, + Predicate[] predicates + ) { + return new CompoundCastPredicate<>(inputType, predicates); + } + + private static class CompoundCastPredicate implements Predicate { + + private final Class inputType; + private final Predicate[] predicates; + + public CompoundCastPredicate(Class inputType, Predicate[] predicates) { + this.inputType = inputType; + this.predicates = predicates; + } + + @Override + public boolean test(InputEvent inputEvent) { + if (!inputType.isInstance(inputEvent)) { + return false; + } + + I castEvent = inputType.cast(inputEvent); + + for (Predicate predicate : predicates) { + if (!predicate.test(castEvent)) { + return false; + } + } + + return true; + } + + } + + private ControlTriggers() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java b/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java index 4943b96..df6c7a8 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/input/KeyMatcher.java @@ -17,46 +17,24 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.input; -import gnu.trove.set.TIntSet; -import ru.windcorp.progressia.client.graphics.backend.InputTracker; +import java.util.function.Predicate; + +import org.lwjgl.glfw.GLFW; public class KeyMatcher { - + private final int key; - private final int[] additionalKeys; private final int mods; - public KeyMatcher(int key, int[] additionalKeys, int mods) { + protected KeyMatcher(int key, int mods) { this.key = key; - this.additionalKeys = additionalKeys; this.mods = mods; } - - public KeyMatcher(KeyEvent template, int... additionalKeys) { - this(template.getKey(), additionalKeys, template.getMods()); - } - - public static KeyMatcher createKeyMatcher(KeyEvent template) { - return new KeyMatcher( - template, - InputTracker.getPressedKeys().toArray() - ); - } public boolean matches(KeyEvent event) { if (!event.isPress()) return false; if (event.getKey() != getKey()) return false; - if (event.getMods() != getMods()) return false; - - TIntSet pressedKeys = InputTracker.getPressedKeys(); - - if (pressedKeys.size() != additionalKeys.length) return false; - - for (int additionalKey : additionalKeys) { - if (!pressedKeys.contains(additionalKey)) { - return false; - } - } + if ((event.getMods() & getMods()) != getMods()) return false; return true; } @@ -65,12 +43,52 @@ public class KeyMatcher { return key; } - public int[] getAdditionalKeys() { - return additionalKeys; - } - public int getMods() { return mods; } + + public static KeyMatcher.Builder of(int key) { + return new KeyMatcher.Builder(key); + } + + public static class Builder { + + private final int key; + private int mods = 0; + + public Builder(int key) { + this.key = key; + } + + public Builder with(int modifier) { + this.mods += modifier; + return this; + } + + public Builder withShift() { + return with(GLFW.GLFW_MOD_SHIFT); + } + + public Builder withCtrl() { + return with(GLFW.GLFW_MOD_CONTROL); + } + + public Builder withAlt() { + return with(GLFW.GLFW_MOD_ALT); + } + + public Builder withSuper() { + return with(GLFW.GLFW_MOD_SUPER); + } + + public KeyMatcher build() { + return new KeyMatcher(key, mods); + } + + public Predicate matcher() { + return build()::matches; + } + + } } diff --git a/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java b/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java index eda8e47..dd752dd 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/CollisionPathComputer.java @@ -10,6 +10,8 @@ import static java.lang.Math.*; public class CollisionPathComputer { + private static final float PADDING = 0.5f; + public static void forEveryBlockInCollisionPath( Collideable coll, float maxTime, @@ -52,18 +54,18 @@ public class CollisionPathComputer { Vec3i pos = Vectors.grab3i(); for ( - pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x)); - pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x)); + pos.x = (int) floor(origin.x + min(0, size.x) + min(0, displacement.x) - PADDING); + pos.x <= (int) ceil(origin.x + max(0, size.x) + max(0, displacement.x) + PADDING); pos.x += 1 ) { for ( - pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y)); - pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y)); + pos.y = (int) floor(origin.y + min(0, size.y) + min(0, displacement.y) - PADDING); + pos.y <= (int) ceil(origin.y + max(0, size.y) + max(0, displacement.y) + PADDING); pos.y += 1 ) { for ( - pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z)); - pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z)); + pos.z = (int) floor(origin.z + min(0, size.z) + min(0, displacement.z) - PADDING); + pos.z <= (int) ceil(origin.z + max(0, size.z) + max(0, displacement.z) + PADDING); pos.z += 1 ) { action.accept(pos); diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java index eb787b3..5e38b90 100644 --- a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java @@ -1,8 +1,8 @@ package ru.windcorp.progressia.common.comms.controls; -import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry; +import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry; -public class ControlDataRegistry extends NamespacedInstanceRegistry { +public class ControlDataRegistry extends NamespacedFactoryRegistry { private static final ControlDataRegistry INSTANCE = new ControlDataRegistry(); diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java index ae708a2..28aca40 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java @@ -6,6 +6,15 @@ import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.comms.Client; public abstract class ControlLogic extends Namespaced { + + @FunctionalInterface + public static interface Lambda { + void apply( + Server server, + PacketControl packet, + Client client + ); + } public ControlLogic(String id) { super(id); @@ -16,5 +25,14 @@ public abstract class ControlLogic extends Namespaced { PacketControl packet, Client client ); + + public static ControlLogic of(String id, Lambda logic) { + return new ControlLogic(id) { + @Override + public void apply(Server server, PacketControl packet, Client client) { + logic.apply(server, packet, client); + } + }; + } } diff --git a/src/main/java/ru/windcorp/progressia/test/ControlBreakBlockData.java b/src/main/java/ru/windcorp/progressia/test/ControlBreakBlockData.java new file mode 100644 index 0000000..23761e5 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/ControlBreakBlockData.java @@ -0,0 +1,22 @@ +package ru.windcorp.progressia.test; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.comms.controls.ControlData; + +public class ControlBreakBlockData extends ControlData { + + private final Vec3i blockInWorld = new Vec3i(); + + public ControlBreakBlockData(String id) { + super(id); + } + + public Vec3i getBlockInWorld() { + return blockInWorld; + } + + public void setBlockInWorld(Vec3i blockInWorld) { + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index ffe6d1b..57d2a72 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -8,8 +8,11 @@ import java.util.function.Consumer; import org.lwjgl.glfw.GLFW; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.comms.controls.*; +import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyMatcher; +import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.world.block.*; import ru.windcorp.progressia.client.world.entity.*; import ru.windcorp.progressia.client.world.tile.*; @@ -21,8 +24,6 @@ import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.*; import ru.windcorp.progressia.common.world.entity.*; import ru.windcorp.progressia.common.world.tile.*; -import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.comms.Client; import ru.windcorp.progressia.server.comms.controls.*; import ru.windcorp.progressia.server.world.block.*; import ru.windcorp.progressia.server.world.entity.*; @@ -88,7 +89,7 @@ public class TestContent { private static void registerEntities() { float scale = 1.8f / 8; - registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); + registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.8f, 0.8f, 1.8f))); register(new TestEntityRenderHuman("Test:Player")); register(new EntityLogic("Test:Player")); @@ -98,25 +99,43 @@ public class TestContent { } private static void regsiterControls() { - ControlDataRegistry.getInstance().register(new ControlData("Test:Switch000")); - ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test:Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches)); - ControlLogicRegistry.getInstance().register(new ControlLogic("Test:Switch000") { - @Override - public void apply(Server server, PacketControl packet, Client client) { - Vec3i z000 = new Vec3i(0, 0, 0); - - ChunkData data = server.getWorld().getChunk(z000).getData(); - - BlockData block; - if (data.getBlock(z000).getId().equals("Test:Stone")) { - block = BlockDataRegistry.getInstance().get("Test:Glass"); - } else { - block = BlockDataRegistry.getInstance().get("Test:Stone"); - } - - server.getAdHocChanger().setBlock(z000, block); + ControlDataRegistry data = ControlDataRegistry.getInstance(); + ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance(); + ControlLogicRegistry logic = ControlLogicRegistry.getInstance(); + + data.register("Test:Switch000", ControlData::new); + triggers.register(ControlTriggers.of( + "Test:Switch000", + KeyEvent.class, + KeyMatcher.of(GLFW.GLFW_KEY_H).matcher() + )); + logic.register(ControlLogic.of("Test:Switch000", (server, packet, client) -> { + Vec3i z000 = new Vec3i(0, 0, 0); + + ChunkData chunk = server.getWorld().getChunk(z000).getData(); + + BlockData block; + if (chunk.getBlock(z000).getId().equals("Test:Stone")) { + block = BlockDataRegistry.getInstance().get("Test:Glass"); + } else { + block = BlockDataRegistry.getInstance().get("Test:Stone"); } - }); + + server.getAdHocChanger().setBlock(z000, block); + })); + + data.register("Test:BreakBlock", ControlBreakBlockData::new); + triggers.register(ControlTriggers.of( + "Test:BreakBlock", + KeyEvent.class, + TestContent::onBlockBreakTrigger, + KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(), + i -> getLookingAt() != null + )); + logic.register(ControlLogic.of("Test:BreakBlock", (server, packet, client) -> { + Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld(); + server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); + })); } private static void register(BlockData x) { @@ -171,5 +190,19 @@ public class TestContent { private static void register(EntityLogic x) { EntityLogicRegistry.getInstance().register(x); } + + private static Vec3i getLookingAt() { + ru.windcorp.progressia.client.Client client = ClientState.getInstance(); + if (client == null) return null; + + LocalPlayer player = client.getLocalPlayer(); + if (player == null) return null; + + return player.getLookingAt(); + } + + private static void onBlockBreakTrigger(ControlData control) { + ((ControlBreakBlockData) control).setBlockInWorld(getLookingAt()); + } } From c5ae4e5ab966f480f9c8c552da2a103153b7e0b1 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 19 Nov 2020 17:51:45 +0300 Subject: [PATCH 19/24] Fixed falling through blocks --- .../common/collision/colliders/Collider.java | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java index 8fdbc90..07c62c4 100644 --- a/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java +++ b/src/main/java/ru/windcorp/progressia/common/collision/colliders/Collider.java @@ -15,6 +15,23 @@ public class Collider { private static final int MAX_COLLISIONS_PER_ENTITY = 64; + /** + * Dear Princess Celestia, + *

+ * When {@linkplain #advanceTime(Collection, Collision, WorldData, float) advancing time}, + * time step for all entities except currently colliding bodies is the current + * collisions's timestamp relative to now. However, currently colliding bodies + * (Collision.a and Collision.b) have a smaller time step. This is done to make sure + * they don't intersect due to rounding errors. + *

+ * Today I learned that bad code has nothing to do with friendship, although lemme tell ya: + * it's got some dank magic. + *

+ * Your faithful student,
+ * Kostyl. + */ + private static final float TIME_STEP_COEFFICIENT_FOR_CURRENTLY_COLLIDING_BODIES = 1e-1f; + public static void performCollisions( List colls, WorldData world, @@ -46,7 +63,7 @@ public class Collider { } } - advanceTime(colls, world, tickLength); + advanceTime(colls, null, world, tickLength); } private static Collision getFirstCollision( @@ -148,7 +165,7 @@ public class Collider { float tickLength, ColliderWorkspace workspace ) { - advanceTime(colls, world, collision.time); + advanceTime(colls, collision, world, collision.time); boolean doNotHandle = false; @@ -274,8 +291,6 @@ public class Collider { collision.a.changeVelocityOnCollision(du_a); collision.b.changeVelocityOnCollision(du_b); - separate(collision, n, m_a, m_b); - // JGML is still to fuck Vectors.release(n); Vectors.release(v_a); @@ -287,25 +302,9 @@ public class Collider { Vectors.release(du_b); } - private static void separate( - Collision collision, - Vec3 normal, float aRelativeMass, float bRelativeMass - ) { - final float margin = 1e-4f; - - Vec3 displacement = Vectors.grab3(); - - displacement.set(normal).mul(margin).mul(bRelativeMass); - collision.a.moveAsCollideable(displacement); - - displacement.set(normal).mul(margin).mul(aRelativeMass).negate(); - collision.b.moveAsCollideable(displacement); - - Vectors.release(displacement); - } - private static void advanceTime( Collection colls, + Collision exceptions, WorldData world, float step ) { @@ -315,7 +314,14 @@ public class Collider { for (Collideable coll : colls) { coll.getCollideableVelocity(tmp); - tmp.mul(step); + + float currentStep = step; + + if (exceptions != null && (exceptions.a == coll || exceptions.b == coll)) { + currentStep *= TIME_STEP_COEFFICIENT_FOR_CURRENTLY_COLLIDING_BODIES; + } + + tmp.mul(currentStep); coll.moveAsCollideable(tmp); } From 996a244649d269db82f8789ea2c215baec51d9f1 Mon Sep 17 00:00:00 2001 From: serega404 Date: Thu, 19 Nov 2020 18:27:44 +0300 Subject: [PATCH 20/24] Added uncaught exception handler --- .../ru/windcorp/progressia/ProgressiaLauncher.java | 13 +++++++++++++ .../progressia/client/ProgressiaClientMain.java | 12 ------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java index d42217b..f9dbaba 100644 --- a/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java +++ b/src/main/java/ru/windcorp/progressia/ProgressiaLauncher.java @@ -17,10 +17,23 @@ *******************************************************************************/ package ru.windcorp.progressia; +import ru.windcorp.progressia.common.util.crash.CrashReports; +import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer; +import ru.windcorp.progressia.common.util.crash.providers.OSContextProvider; + public class ProgressiaLauncher { public static void launch(String[] args, Proxy proxy) { + setupCrashReports(); proxy.initialize(); } + private static void setupCrashReports() { + CrashReports.registerProvider(new OSContextProvider()); + CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer()); + Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> { + CrashReports.report(t,"Uncaught exception in thread %s", thread.getName()); + }); + } + } diff --git a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java index 0b755f0..0ab1bd0 100644 --- a/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java +++ b/src/main/java/ru/windcorp/progressia/client/ProgressiaClientMain.java @@ -20,9 +20,6 @@ package ru.windcorp.progressia.client; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import ru.windcorp.progressia.ProgressiaLauncher; -import ru.windcorp.progressia.common.util.crash.CrashReports; -import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer; -import ru.windcorp.progressia.common.util.crash.providers.OSContextProvider; public class ProgressiaClientMain { @@ -31,15 +28,6 @@ public class ProgressiaClientMain { public static void main(String[] args) { logger.info("App started!"); - CrashReports.registerProvider(new OSContextProvider()); - CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer()); - try { - @SuppressWarnings("unused") - long[] ssdss = new long[1 << 30]; - } catch (Throwable t) { - CrashReports.report(t, "u %s stupid", "vry"); - } - ProgressiaLauncher.launch(args, new ClientProxy()); } From afa1234ddd48fae6fe107ba37a1493a165cf251f Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 19 Nov 2020 18:59:41 +0300 Subject: [PATCH 21/24] Fixed English in messages --- .../comms/DefaultClientCommsListener.java | 8 ++- .../graphics/backend/shaders/Program.java | 2 +- .../backend/shaders/attributes/Attribute.java | 2 +- .../backend/shaders/uniforms/Uniform.java | 2 +- .../graphics/font/GNUUnifontLoader.java | 56 ++++++++++--------- .../client/graphics/gui/Component.java | 10 ++-- .../client/graphics/texture/Atlases.java | 4 +- .../graphics/texture/SimpleTextures.java | 2 +- .../graphics/texture/TexturePrimitive.java | 2 +- .../client/localization/Parser.java | 2 +- .../world/entity/EntityRenderRegistry.java | 2 +- .../progressia/common/resource/Resource.java | 4 +- .../world/ImplementedChangeTracker.java | 4 +- 13 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index b2c7051..4053672 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -1,6 +1,8 @@ package ru.windcorp.progressia.client.comms; import java.io.IOException; + +import ru.windcorp.jputil.chars.StringUtil; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.client.world.ChunkRender; @@ -42,7 +44,11 @@ public class DefaultClientCommsListener implements CommsListener { ); if (entity == null) { - CrashReports.report(null, "Player entity not found"); + CrashReports.report( + null, + "Player entity with ID %s not found", + new String(StringUtil.toFullHex(packet.getLocalPlayerEntityId())) + ); } getClient().setLocalPlayer(entity); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java index 64c54bc..2b63885 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/Program.java @@ -40,7 +40,7 @@ public class Program implements OpenGLDeletable { glLinkProgram(handle); if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) { - CrashReports.report(null, "Bad program:\n %s", glGetProgramInfoLog(handle)); + CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle)); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java index 34624f4..9075085 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/attributes/Attribute.java @@ -27,7 +27,7 @@ public class Attribute { public Attribute(int handle, Program program) { if (handle < 0) { - CrashReports.report(null, "Bad handle: %s", handle); + CrashReports.report(null, "Bad handle: %d", handle); } this.handle = handle; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java index d1475b0..996b1fe 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/shaders/uniforms/Uniform.java @@ -27,7 +27,7 @@ public class Uniform { public Uniform(int handle, Program program) { if (handle < 0) { - CrashReports.report(null, "Bad handle: %s", handle); + CrashReports.report(null, "Bad handle: %d", handle); } this.handle = handle; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java index 61e9666..72f359c 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/font/GNUUnifontLoader.java @@ -57,7 +57,7 @@ public class GNUUnifontLoader { return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas) .collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new)); } catch (IOException | UncheckedIOException e) { - CrashReports.report(e, "Problem with load GNUUnifont"); + CrashReports.report(e, "Could not load GNUUnifont"); return null; } } @@ -72,23 +72,30 @@ public class GNUUnifontLoader { } private static ParsedGlyph parse(String declar) { - int width = getWidth(declar); - checkDeclaration(declar, width); - - char c = getChar(declar); - - TextureDataEditor editor = new TextureDataEditor(width, GNUUnifont.HEIGHT, width, GNUUnifont.HEIGHT, - TEXTURE_SETTINGS); - - for (int y = 0; y < GNUUnifont.HEIGHT; ++y) { - for (int x = 0; x < width; ++x) { - int bit = x + y * width; - - editor.setPixel(x, GNUUnifont.HEIGHT - y - 1, getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000); + try { + + int width = getWidth(declar); + checkDeclaration(declar, width); + + char c = getChar(declar); + + TextureDataEditor editor = new TextureDataEditor(width, GNUUnifont.HEIGHT, width, GNUUnifont.HEIGHT, + TEXTURE_SETTINGS); + + for (int y = 0; y < GNUUnifont.HEIGHT; ++y) { + for (int x = 0; x < width; ++x) { + int bit = x + y * width; + + editor.setPixel(x, GNUUnifont.HEIGHT - y - 1, getBit(declar, bit) ? 0xFFFFFFFF : 0x00000000); + } } + + return new ParsedGlyph(c, editor); + + } catch (IOException e) { + CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar); + return null; } - - return new ParsedGlyph(c, editor); } private static char getChar(String declar) { @@ -117,26 +124,25 @@ public class GNUUnifontLoader { return meaningfulChars / charsPerColumn; } - private static void checkDeclaration(String declar, int width) { + private static void checkDeclaration(String declar, int width) throws IOException { if (!GNUUnifont.WIDTHS.contains(width)) { - CrashReports.report(null, "Width %d is not supported (in declar \"%s\")", width, declar); + throw new IOException("Width " + width + " is not supported (in declar \"" + declar + "\")"); } - + if ((declar.length() - PREFIX_LENGTH) % width != 0) { - CrashReports.report(null, "Declar \"%s\" has invalid length", declar); + throw new IOException("Declar \"" + declar + "\" has invalid length"); } - + for (int i = 0; i < declar.length(); ++i) { if (i == BITS_PER_HEX_DIGIT) { if (declar.charAt(i) != ':') { - CrashReports.report(null, "No colon ':' found in declar \"%s\" at index 4", declar); + throw new IOException("No colon ':' found in declar \"" + declar + "\" at index 4"); } } else { char c = declar.charAt(i); - + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))) { - CrashReports.report(null, - "Illegal char in declar \"%s\" at index " + i + "; expected 0-9A-F", declar); + throw new IOException("Illegal char in declar \"" + declar + "\" at index " + i + "; expected 0-9A-F"); } } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java index 7ba1b83..1a1b1f4 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java @@ -235,7 +235,7 @@ public class Component extends Named { valid = true; } catch (Exception e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not layout Component %s", this); } } @@ -248,7 +248,7 @@ public class Component extends Named { try { return getLayout().calculatePreferredSize(this); } catch (Exception e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not calculate preferred size for Component %s", this); } } @@ -517,7 +517,7 @@ public class Component extends Named { break; } } catch (Exception e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not dispatch input to Component %s", this); } } @@ -609,7 +609,7 @@ public class Component extends Named { try { assembleSelf(target); } catch (Exception e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not assemble Component %s", this); } assembleChildren(target); @@ -617,7 +617,7 @@ public class Component extends Named { try { postAssembleSelf(target); } catch (Exception e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Post-assembly failed for Component %s", this); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java index a945f03..0ce64d4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/Atlases.java @@ -157,7 +157,7 @@ public class Atlases { return loadSprite(data.getData(), group); } catch (IOException e) { - CrashReports.report(e, "Problem with load atlases sprite"); + CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group); return null; } } @@ -174,7 +174,7 @@ public class Atlases { Atlas newAtlas = new Atlas(group); if (!newAtlas.canAddSprite(data)) { - CrashReports.report(null, "Could not fit tex into atlas of size %d", newAtlas.getSize()); + CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize()); } atlases.add(newAtlas); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java index e0711db..51a1235 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/SimpleTextures.java @@ -31,7 +31,7 @@ public class SimpleTextures { new Sprite(new TexturePrimitive(data.getData())) ); } catch (IOException e) { - CrashReports.report(e, "Problem with load texture"); + CrashReports.report(e, "Could not load texture %s", resource); return null; } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java index e4f5fdc..b4df4fb 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/TexturePrimitive.java @@ -90,7 +90,7 @@ public class TexturePrimitive implements OpenGLDeletable { OpenGLObjectTracker.register(this); if (handle < 0) { - CrashReports.report(null, "Failed to create texture"); + CrashReports.report(null, "Failed to allocate texture"); } } diff --git a/src/main/java/ru/windcorp/progressia/client/localization/Parser.java b/src/main/java/ru/windcorp/progressia/client/localization/Parser.java index c0438d5..239c779 100644 --- a/src/main/java/ru/windcorp/progressia/client/localization/Parser.java +++ b/src/main/java/ru/windcorp/progressia/client/localization/Parser.java @@ -81,7 +81,7 @@ public class Parser { } } catch (IOException | EscapeException e) { - CrashReports.report(e, "Problems with parsing"); + CrashReports.report(e, "Could not parse language file %s", filePath); return null; } return parsedData; diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java index 1e33fd3..ad0c161 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -29,7 +29,7 @@ public class EntityRenderRegistry extends NamespacedRegistry { ).getData() ); } catch (IOException e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not load entity texture %s", name); return null; } } diff --git a/src/main/java/ru/windcorp/progressia/common/resource/Resource.java b/src/main/java/ru/windcorp/progressia/common/resource/Resource.java index d2e42ea..43b1839 100644 --- a/src/main/java/ru/windcorp/progressia/common/resource/Resource.java +++ b/src/main/java/ru/windcorp/progressia/common/resource/Resource.java @@ -51,7 +51,7 @@ public class Resource extends Named { try (Reader reader = getReader()) { return CharStreams.toString(reader); } catch (IOException e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not read resource %s as text", this); return null; } } @@ -61,7 +61,7 @@ public class Resource extends Named { try (InputStream stream = getInputStream()) { byteArray = ByteStreams.toByteArray(stream); } catch (IOException e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not read resource %s as bytes", this); return null; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java index 960f77d..f277cb1 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java @@ -134,7 +134,7 @@ public class ImplementedChangeTracker implements Changer { try { entity.write(packet.getWriter(), IOContext.COMMS); } catch (IOException e) { - CrashReports.report(e, "__DOC__ME__"); + CrashReports.report(e, "Could not write entity %s", entity); } } @@ -146,7 +146,7 @@ public class ImplementedChangeTracker implements Changer { try { entity.write(packet.getWriter(), IOContext.COMMS); } catch (IOException e) { - CrashReports.report(e, "Entity could not be written"); + CrashReports.report(e, "Could not write entity %s", entity); } } From 7f381c7a1fe7b8bdbc067aa2afa744f40aaf33e8 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 20 Nov 2020 11:50:24 +0300 Subject: [PATCH 22/24] Updated block selection display and added block placing - Packaged everything related to block selection into Selection class - Selection box is a bit less obnoxious - Added the ability to place Test:Stone blocks - This is technically a Minecraft clone now. I don't know how to feel about this tbh --- .../client/graphics/world/LayerWorld.java | 45 ++++++++++-- .../client/graphics/world/LocalPlayer.java | 44 ++---------- .../client/graphics/world/Selection.java | 72 +++++++++++++++++++ .../progressia/common/world/BlockRay.java | 35 +++++++-- .../progressia/common/world/WorldData.java | 11 +++ .../common/world/block/BlockFace.java | 25 +++++-- .../test/ControlPlaceBlockData.java | 22 ++++++ .../windcorp/progressia/test/TestContent.java | 47 +++++++++--- 8 files changed, 237 insertions(+), 64 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/world/Selection.java create mode 100644 src/main/java/ru/windcorp/progressia/test/ControlPlaceBlockData.java diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 2e4b6be..1533246 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -20,6 +20,8 @@ package ru.windcorp.progressia.client.graphics.world; import java.util.ArrayList; import java.util.List; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.ClientState; @@ -28,9 +30,15 @@ import ru.windcorp.progressia.client.graphics.Layer; import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram; +import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; +import ru.windcorp.progressia.client.graphics.model.StaticModel; +import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.colliders.Collider; +import ru.windcorp.progressia.common.util.FloatMathUtils; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.test.CollisionModelRenderer; import ru.windcorp.progressia.test.TestPlayerControls; @@ -127,19 +135,48 @@ public class LayerWorld extends Layer { tmp_colliderWorkspace ); } + + private static final Renderable SELECTION_BOX = tmp_createSelectionBox(); private void tmp_drawSelectionBox() { LocalPlayer player = client.getLocalPlayer(); if (player == null) return; - Vec3i lookingAt = player.getLookingAt(); - if (lookingAt == null) return; + Vec3i selection = player.getSelection().getBlock(); + if (selection == null) return; - helper.pushTransform().translate(lookingAt.x, lookingAt.y, lookingAt.z).scale(1.1f); - CollisionModelRenderer.renderCollisionModel(client.getWorld().getData().getCollisionModelOfBlock(lookingAt), helper); + helper.pushTransform().translate(selection.x, selection.y, selection.z); + SELECTION_BOX.render(helper); helper.popTransform(); } + private static Renderable tmp_createSelectionBox() { + StaticModel.Builder b = StaticModel.builder(); + ShapeRenderProgram p = WorldRenderProgram.getDefault(); + + final float f = 1e-2f; + final float scale = 1 - f/2; + final Vec3 color = new Vec3(1, 1, 1).mul(0); + + for (float phi = 0; phi < 2*FloatMathUtils.PI_F; phi += FloatMathUtils.PI_F/2) { + Mat4 rot = new Mat4().identity().rotateZ(phi).scale(scale); + + b.addPart(new PppBuilder(p, (Texture) null).setOrigin( + new Vec3(-f - 0.5f, -f - 0.5f, -f - 0.5f) + ).setSize(f, f, 2*f + 1).setColorMultiplier(color).create(), rot); + + b.addPart(new PppBuilder(p, (Texture) null).setOrigin( + new Vec3(-f - 0.5f, - 0.5f, -f - 0.5f) + ).setSize(f, 1, f).setColorMultiplier(color).create(), rot); + + b.addPart(new PppBuilder(p, (Texture) null).setOrigin( + new Vec3(-f - 0.5f, - 0.5f, + 0.5f) + ).setSize(f, 1, f).setColorMultiplier(color).create(), rot); + } + + return new StaticModel(b); + } + private void tmp_applyFriction(EntityData entity) { final float frictionCoeff = 1 - 1e-5f; entity.getVelocity().mul(frictionCoeff); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java index 5df2b84..9179bc4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java @@ -1,60 +1,24 @@ package ru.windcorp.progressia.client.graphics.world; -import glm.vec._3.Vec3; -import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.entity.EntityRenderable; -import ru.windcorp.progressia.common.util.Vectors; -import ru.windcorp.progressia.common.world.BlockRay; import ru.windcorp.progressia.common.world.Player; import ru.windcorp.progressia.common.world.entity.EntityData; public class LocalPlayer extends Player { - private Vec3i lookingAt = new Vec3i(); - private boolean isLookingAtBlock = false; - - private BlockRay lookingAtRay = new BlockRay(); + private final Selection selection = new Selection(); public LocalPlayer(EntityData entity) { super(entity); } - public Vec3i getLookingAt() { - return isLookingAtBlock ? lookingAt : null; + public Selection getSelection() { + return selection; } public void update(WorldRender world) { - updateLookingAt(world); - } - - private void updateLookingAt(WorldRender world) { - Vec3 direction = Vectors.grab3(); - Vec3 start = Vectors.grab3(); - - BlockRay ray = lookingAtRay; - EntityData player = getEntity(); - - player.getLookingAtVector(direction); - world.getEntityRenderable(player).getViewPoint(start); - start.add(player.getPosition()); - - isLookingAtBlock = false; - - for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) { - Vec3i blockInWorld = ray.current(); - - if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) { - isLookingAtBlock = true; - lookingAt.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); - break; - } - } - - ray.end(); - - Vectors.release(direction); - Vectors.release(start); + getSelection().update(world, getEntity()); } public EntityRenderable getRenderable(WorldRender world) { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/Selection.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/Selection.java new file mode 100644 index 0000000..3627e3c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/Selection.java @@ -0,0 +1,72 @@ +package ru.windcorp.progressia.client.graphics.world; + +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.world.WorldRender; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.BlockRay; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class Selection { + + private final Vec3i block = new Vec3i(); + private BlockFace surface = null; + private final Vec2 pointOnSurface = new Vec2(0.5f, 0.5f); + private final Vec3 point = new Vec3(); + + private boolean exists = false; + + private BlockRay ray = new BlockRay(); + + public void update(WorldRender world, EntityData player) { + Vec3 direction = Vectors.grab3(); + Vec3 start = Vectors.grab3(); + + player.getLookingAtVector(direction); + world.getEntityRenderable(player).getViewPoint(start); + start.add(player.getPosition()); + + exists = false; + + for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) { + Vec3i blockInWorld = ray.current(); + + if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) { + exists = true; + block.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + ray.getPoint(point); + surface = ray.getCurrentFace(); + // TODO selectedPointOnSurface + break; + } + } + + ray.end(); + + Vectors.release(direction); + Vectors.release(start); + } + + public Vec3i getBlock() { + return exists ? block : null; + } + + public Vec3 getPoint() { + return exists ? point : null; + } + + public BlockFace getSurface() { + return exists ? surface : null; + } + + public Vec2 getPointOnSurface() { + return exists ? pointOnSurface : null; + } + + public boolean exists() { + return exists; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java index 158a1fc..ab4dbf7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java +++ b/src/main/java/ru/windcorp/progressia/common/world/BlockRay.java @@ -4,6 +4,7 @@ import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.VectorUtil.Axis; +import ru.windcorp.progressia.common.world.block.BlockFace; import static java.lang.Math.*; @@ -15,6 +16,7 @@ public class BlockRay { private float distance; private final Vec3i block = new Vec3i(); + private BlockFace currentFace = null; private boolean isValid = false; @@ -65,11 +67,8 @@ public class BlockRay { // position.(axis) = round(position.(axis)) VectorUtil.set(position, axis, round(VectorUtil.get(position, axis))); - return block; - } - - public Vec3i current() { - checkState(); + this.currentFace = computeCurrentFace(axis, (int) signum(VectorUtil.get(direction, axis))); + return block; } @@ -87,6 +86,32 @@ public class BlockRay { return (edge - c) / dir; } + private BlockFace computeCurrentFace(Axis axis, int sign) { + if (sign == 0) throw new IllegalStateException("sign is zero"); + + switch (axis) { + case X: return sign > 0 ? BlockFace.SOUTH : BlockFace.NORTH; + case Y: return sign > 0 ? BlockFace.EAST : BlockFace.WEST; + default: + case Z: return sign > 0 ? BlockFace.BOTTOM : BlockFace.TOP; + } + } + + public Vec3i current() { + checkState(); + return block; + } + + public Vec3 getPoint(Vec3 output) { + output.set(position); + output.add(0.5f); // Make sure we're in the block-center coordinate system + return output; + } + + public BlockFace getCurrentFace() { + return currentFace; + } + public float getDistance() { checkState(); return distance; diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 6584884..a8a4966 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -88,6 +88,17 @@ public class WorldData { return result; } + public BlockData getBlock(Vec3i blockInWorld) { + ChunkData chunk = getChunkByBlock(blockInWorld); + if (chunk == null) return null; + + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk); + BlockData result = chunk.getBlock(blockInChunk); + + return result; + } + public Collection getChunks() { return chunks; } diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java index f33f8d7..461429e 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java @@ -25,12 +25,12 @@ import glm.vec._3.i.Vec3i; public final class BlockFace extends BlockRelation { public static final BlockFace - TOP = new BlockFace( 0, 0, +1, true), - BOTTOM = new BlockFace( 0, 0, -1, false), - NORTH = new BlockFace(+1, 0, 0, true), - SOUTH = new BlockFace(-1, 0, 0, false), - WEST = new BlockFace( 0, +1, 0, false), - EAST = new BlockFace( 0, -1, 0, true); + TOP = new BlockFace( 0, 0, +1, true, "TOP"), + BOTTOM = new BlockFace( 0, 0, -1, false, "BOTTOM"), + NORTH = new BlockFace(+1, 0, 0, true, "NORTH"), + SOUTH = new BlockFace(-1, 0, 0, false, "SOUTH"), + WEST = new BlockFace( 0, +1, 0, false, "WEST"), + EAST = new BlockFace( 0, -1, 0, true, "EAST"); private static final ImmutableList ALL_FACES = ImmutableList.of(TOP, BOTTOM, NORTH, SOUTH, WEST, EAST); @@ -77,15 +77,21 @@ public final class BlockFace extends BlockRelation { private static int nextId = 0; private final int id; + private final String name; private BlockFace counterFace; private final boolean isPrimary; - private BlockFace(int x, int y, int z, boolean isPrimary) { + private BlockFace(int x, int y, int z, boolean isPrimary, String name) { super(x, y, z); this.id = nextId++; this.isPrimary = isPrimary; + this.name = name; } + public String getName() { + return name; + } + public boolean isPrimary() { return isPrimary; } @@ -132,5 +138,10 @@ public final class BlockFace extends BlockRelation { public int getManhattanDistance() { return 1; } + + @Override + public String toString() { + return getName(); + } } diff --git a/src/main/java/ru/windcorp/progressia/test/ControlPlaceBlockData.java b/src/main/java/ru/windcorp/progressia/test/ControlPlaceBlockData.java new file mode 100644 index 0000000..82dc80b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/ControlPlaceBlockData.java @@ -0,0 +1,22 @@ +package ru.windcorp.progressia.test; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.comms.controls.ControlData; + +public class ControlPlaceBlockData extends ControlData { + + private final Vec3i blockInWorld = new Vec3i(); + + public ControlPlaceBlockData(String id) { + super(id); + } + + public Vec3i getBlockInWorld() { + return blockInWorld; + } + + public void setBlockInWorld(Vec3i blockInWorld) { + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 57d2a72..a7a5027 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.comms.controls.*; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.graphics.world.LocalPlayer; +import ru.windcorp.progressia.client.graphics.world.Selection; import ru.windcorp.progressia.client.world.block.*; import ru.windcorp.progressia.client.world.entity.*; import ru.windcorp.progressia.client.world.tile.*; @@ -20,10 +21,12 @@ import ru.windcorp.progressia.common.collision.AABB; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.*; import ru.windcorp.progressia.common.world.entity.*; import ru.windcorp.progressia.common.world.tile.*; +import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.comms.controls.*; import ru.windcorp.progressia.server.world.block.*; import ru.windcorp.progressia.server.world.entity.*; @@ -130,12 +133,19 @@ public class TestContent { KeyEvent.class, TestContent::onBlockBreakTrigger, KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(), - i -> getLookingAt() != null + i -> getSelection().exists() )); - logic.register(ControlLogic.of("Test:BreakBlock", (server, packet, client) -> { - Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld(); - server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); - })); + logic.register(ControlLogic.of("Test:BreakBlock", TestContent::onBlockBreakReceived)); + + data.register("Test:PlaceBlock", ControlPlaceBlockData::new); + triggers.register(ControlTriggers.of( + "Test:PlaceBlock", + KeyEvent.class, + TestContent::onBlockPlaceTrigger, + KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_RIGHT).matcher(), + i -> getSelection().exists() + )); + logic.register(ControlLogic.of("Test:PlaceBlock", TestContent::onBlockPlaceReceived)); } private static void register(BlockData x) { @@ -191,18 +201,39 @@ public class TestContent { EntityLogicRegistry.getInstance().register(x); } - private static Vec3i getLookingAt() { + private static Selection getSelection() { ru.windcorp.progressia.client.Client client = ClientState.getInstance(); if (client == null) return null; LocalPlayer player = client.getLocalPlayer(); if (player == null) return null; - return player.getLookingAt(); + return player.getSelection(); } private static void onBlockBreakTrigger(ControlData control) { - ((ControlBreakBlockData) control).setBlockInWorld(getLookingAt()); + ((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock()); + } + + private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) { + Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld(); + server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); + } + + private static void onBlockPlaceTrigger(ControlData control) { + Vec3i blockInWorld = Vectors.grab3i(); + Vec3i selectedBlock = getSelection().getBlock(); + + blockInWorld.set(selectedBlock.x, selectedBlock.y, selectedBlock.z).add(getSelection().getSurface().getVector()); + + ((ControlPlaceBlockData) control).setBlockInWorld(blockInWorld); + Vectors.release(blockInWorld); + } + + private static void onBlockPlaceReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) { + Vec3i blockInWorld = ((ControlPlaceBlockData) packet.getControl()).getBlockInWorld(); + if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) return; + server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Stone")); } } From 372f173723305dc9c34a2b5d77f16467d2d0fa76 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 20 Nov 2020 23:54:32 +0300 Subject: [PATCH 23/24] Introduced ChunkData and WorldDataListeners - Added ChunkDataListener and WorldDataListener - Moved chunk render update requests to clientside ChunkDataListeners - Moved ChunkRender and ChunkLogic initialization into WDListeners --- .../progressia/client/ClientState.java | 2 + .../comms/DefaultClientCommsListener.java | 10 ---- .../client/world/ChunkUpdateListener.java | 19 +++++++ .../progressia/client/world/WorldRender.java | 17 +++++- .../progressia/common/world/ChunkData.java | 46 ++++++++++++--- .../common/world/ChunkDataListener.java | 51 +++++++++++++++++ .../common/world/ChunkDataListeners.java | 25 ++++++++ .../progressia/common/world/Coordinates.java | 9 ++- .../world/IllegalCoordinatesException.java | 28 +++++++++ .../progressia/common/world/WorldData.java | 57 +++++++++++++++++-- .../common/world/WorldDataListener.java | 34 +++++++++++ .../progressia/server/ServerState.java | 1 + .../progressia/server/world/WorldLogic.java | 37 +++++++++++- ...datableBlock.java => UpdateableBlock.java} | 0 14 files changed, 303 insertions(+), 33 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/world/ChunkUpdateListener.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/ChunkDataListener.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/ChunkDataListeners.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/IllegalCoordinatesException.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java rename src/main/java/ru/windcorp/progressia/server/world/block/{UpdatableBlock.java => UpdateableBlock.java} (100%) diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index b812907..60d5ca2 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -30,6 +30,8 @@ public class ClientState { Client client = new Client(world, channel); + world.tmp_generate(); + channel.connect(); setInstance(client); diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index 4053672..f2eb76a 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -5,14 +5,12 @@ import java.io.IOException; import ru.windcorp.jputil.chars.StringUtil; import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.graphics.world.EntityAnchor; -import ru.windcorp.progressia.client.world.ChunkRender; import ru.windcorp.progressia.common.comms.CommsListener; import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.common.world.entity.PacketEntityChange; // TODO refactor with no mercy public class DefaultClientCommsListener implements CommsListener { @@ -29,10 +27,6 @@ public class DefaultClientCommsListener implements CommsListener { ((PacketWorldChange) packet).apply( getClient().getWorld().getData() ); - - if (!(packet instanceof PacketEntityChange)) { - tmp_reassembleWorld(); - } } else if (packet instanceof PacketSetLocalPlayer) { setLocalPlayer((PacketSetLocalPlayer) packet); } @@ -57,10 +51,6 @@ public class DefaultClientCommsListener implements CommsListener { )); } - private void tmp_reassembleWorld() { - getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate); - } - @Override public void onIOError(IOException reason) { // TODO implement diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkUpdateListener.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkUpdateListener.java new file mode 100644 index 0000000..d5fd908 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkUpdateListener.java @@ -0,0 +1,19 @@ +package ru.windcorp.progressia.client.world; + +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.ChunkDataListener; + +class ChunkUpdateListener implements ChunkDataListener { + + private final WorldRender world; + + public ChunkUpdateListener(WorldRender world) { + this.world = world; + } + + @Override + public void onChunkChanged(ChunkData chunk) { + world.getChunk(chunk).markForUpdate(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index fc219f4..49b8272 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -29,7 +29,9 @@ import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.ChunkDataListeners; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.WorldDataListener; import ru.windcorp.progressia.common.world.entity.EntityData; public class WorldRender { @@ -43,9 +45,18 @@ public class WorldRender { public WorldRender(WorldData data) { this.data = data; - for (ChunkData chunkData : data.getChunks()) { - chunks.put(chunkData, new ChunkRender(this, chunkData)); - } + data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this))); + data.addListener(new WorldDataListener() { + @Override + public void onChunkLoaded(WorldData world, ChunkData chunk) { + chunks.put(chunk, new ChunkRender(WorldRender.this, chunk)); + } + + @Override + public void beforeChunkUnloaded(WorldData world, ChunkData chunk) { + chunks.remove(chunk); + } + }); } public WorldData getData() { diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 031be35..d725193 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.common.world; import static ru.windcorp.progressia.common.world.block.BlockFace.*; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; @@ -63,8 +64,11 @@ public class ChunkData { private final List entities = Collections.synchronizedList(new ArrayList<>()); - public ChunkData(int x, int y, int z, WorldData world) { - this.position.set(x, y, z); + private final Collection listeners = + Collections.synchronizedCollection(new ArrayList<>()); + + public ChunkData(Vec3i position, WorldData world) { + this.position.set(position.x, position.y, position.z); this.world = world; tmp_generate(); @@ -92,11 +96,11 @@ public class ChunkData { pos.set(x, y, z); if (f > 17) { - setBlock(pos, stone); + setBlock(pos, stone, false); } else if (f > 14) { - setBlock(pos, dirt); + setBlock(pos, dirt, false); } else { - setBlock(pos, air); + setBlock(pos, air, false); } } @@ -152,8 +156,16 @@ public class ChunkData { return blocks[getBlockIndex(posInChunk)]; } - public void setBlock(Vec3i posInChunk, BlockData block) { + public void setBlock(Vec3i posInChunk, BlockData block, boolean notify) { + BlockData previous = blocks[getBlockIndex(posInChunk)]; blocks[getBlockIndex(posInChunk)] = block; + + if (notify) { + getListeners().forEach(l -> { + l.onChunkBlockChanged(this, posInChunk, previous, block); + l.onChunkChanged(this); + }); + } } public List getTilesOrNull(Vec3i blockInChunk, BlockFace face) { @@ -245,7 +257,7 @@ public class ChunkData { private static void checkLocalCoordinates(Vec3i posInChunk) { if (!isInBounds(posInChunk)) { - throw new IllegalArgumentException( + throw new IllegalCoordinatesException( "Coordinates " + str(posInChunk) + " " + "are not legal chunk coordinates" ); @@ -331,8 +343,28 @@ public class ChunkData { return world; } + public Collection getListeners() { + return listeners; + } + + public void addListener(ChunkDataListener listener) { + this.listeners.add(listener); + } + + public void removeListener(ChunkDataListener listener) { + this.listeners.remove(listener); + } + private static String str(Vec3i v) { return "(" + v.x + "; " + v.y + "; " + v.z + ")"; } + protected void onLoaded() { + getListeners().forEach(l -> l.onChunkLoaded(this)); + } + + protected void beforeUnloaded() { + getListeners().forEach(l -> l.beforeChunkUnloaded(this)); + } + } diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListener.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListener.java new file mode 100644 index 0000000..216617e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListener.java @@ -0,0 +1,51 @@ +package ru.windcorp.progressia.common.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; + +public interface ChunkDataListener { + + /** + * Invoked after a block has changed in a chunk. + * This is not triggered when a change is caused by chunk loading or unloading. + * @param chunk the chunk that has changed + * @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change + * @param previous the previous occupant of {@code blockInChunk} + * @param current the current (new) occupant of {@code blockInChunk} + */ + default void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {} + + /** + * Invoked after a tile has been added or removed from a chunk. + * This is not triggered when a change is caused by chunk loading or unloading. + * @param chunk the chunk that has changed + * @param blockInChunk the {@linkplain Coordinates#blockInChunk chunk coordinates} of the change + * @param face the face that the changed tile belongs or belonged to + * @param tile the tile that has been added or removed + * @param wasAdded {@code true} iff the tile has been added, {@code false} iff the tile has been removed + */ + default void onChunkTilesChanged(ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile, boolean wasAdded) {} + + /** + * Invoked whenever a chunk changes, loads or unloads. If some other method in this + * {@code ChunkDataListener} are to be invoked, e.g. is the change was caused by a + * block being removed, this method is called last. + * @param chunk the chunk that has changed + */ + default void onChunkChanged(ChunkData chunk) {} + + /** + * Invoked whenever a chunk has been loaded. + * @param chunk the chunk that has loaded + */ + default void onChunkLoaded(ChunkData chunk) {} + + /** + * Invoked whenever a chunk is about to be unloaded. + * @param chunk the chunk that is going to be loaded + */ + default void beforeChunkUnloaded(ChunkData chunk) {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListeners.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListeners.java new file mode 100644 index 0000000..e0ca46a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkDataListeners.java @@ -0,0 +1,25 @@ +package ru.windcorp.progressia.common.world; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import glm.vec._3.i.Vec3i; + +public class ChunkDataListeners { + + public static WorldDataListener createAdder(Supplier listenerSupplier) { + return new WorldDataListener() { + @Override + public void getChunkListeners(WorldData world, Vec3i chunk, Consumer chunkListenerSink) { + chunkListenerSink.accept(listenerSupplier.get()); + } + }; + } + + public static WorldDataListener createAdder(ChunkDataListener listener) { + return createAdder(() -> listener); + } + + private ChunkDataListeners() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java index 76a3f95..bfc7773 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java +++ b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java @@ -11,18 +11,17 @@ import glm.vec._3.i.Vec3i; * Three types of coordinates are used in Progressia: *

    * - *
  • World coordinates, in code referred to as {@code blockInWorld} - + *
  • World coordinates, in code referred to as {@code blockInWorld} - * coordinates relative to world origin. Every block in the world has unique * world coordinates.
  • * - *
  • Chunk coordinates, in code referred to as {@code blockInChunk} - + *
  • Chunk coordinates, in code referred to as {@code blockInChunk} - * coordinates relative some chunk's origin. Every block in the chunk has unique * chunk coordinates, but blocks in different chunks may have identical chunk * coordinates. These coordinates are only useful in combination with a chunk - * reference. Chunk coordinates are always [0; {@link #BLOCKS_PER_CHUNK}) - * .
  • + * reference. Chunk coordinates are always [0; {@link #BLOCKS_PER_CHUNK}). * - *
  • Coordinates of chunk, in code referred to as {@code chunk} - + *
  • Coordinates of chunk, in code referred to as {@code chunk} - * chunk coordinates relative to world origin. Every chunk in the world has * unique coordinates of chunk.
  • * diff --git a/src/main/java/ru/windcorp/progressia/common/world/IllegalCoordinatesException.java b/src/main/java/ru/windcorp/progressia/common/world/IllegalCoordinatesException.java new file mode 100644 index 0000000..aba1390 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/IllegalCoordinatesException.java @@ -0,0 +1,28 @@ +package ru.windcorp.progressia.common.world; + +public class IllegalCoordinatesException extends RuntimeException { + + private static final long serialVersionUID = 1362481281554206710L; + + public IllegalCoordinatesException() { + super(); + } + + public IllegalCoordinatesException(String message) { + super(message); + } + + public IllegalCoordinatesException(Throwable cause) { + super(cause); + } + + public IllegalCoordinatesException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalCoordinatesException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index a8a4966..29e82ce 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -17,6 +17,7 @@ *******************************************************************************/ package ru.windcorp.progressia.common.world; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,30 +47,50 @@ public class WorldData { private float time = 0; + private final Collection listeners = + Collections.synchronizedCollection(new ArrayList<>()); + public WorldData() { - final int size = 1; - for (int x = -(size / 2); x <= (size / 2); ++x) { - for (int y = -(size / 2); y <= (size / 2); ++y) { - addChunk(new ChunkData(x, y, 0, this)); + } + + public void tmp_generate() { + final int size = 1; + Vec3i cursor = new Vec3i(0, 0, 0); + + for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { + for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) { + ChunkData chunk = new ChunkData(cursor, this); + addChunkListeners(chunk); + addChunk(chunk); } } } + private void addChunkListeners(ChunkData chunk) { + getListeners().forEach(l -> l.getChunkListeners(this, chunk.getPosition(), chunk::addListener)); + } + private synchronized void addChunk(ChunkData chunk) { chunksByPos.put(getChunkKey(chunk), chunk); chunk.forEachEntity(entity -> entitiesById.put(entity.getEntityId(), entity) ); + + chunk.onLoaded(); + getListeners().forEach(l -> l.onChunkLoaded(this, chunk)); } // private synchronized void removeChunk(ChunkData chunk) { -// chunksByPos.remove(getChunkKey(chunk)); +// getListeners().forEach(l -> l.beforeChunkUnloaded(this, chunk)); +// chunk.beforeUnloaded(); // // chunk.forEachEntity(entity -> // entitiesById.remove(entity.getEntityId()) // ); +// +// chunksByPos.remove(getChunkKey(chunk)); // } private static long getChunkKey(ChunkData chunk) { @@ -99,6 +120,20 @@ public class WorldData { return result; } + public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) { + ChunkData chunk = getChunkByBlock(blockInWorld); + if (chunk == null) + throw new IllegalCoordinatesException( + "Coordinates " + + "(" + blockInWorld.x + "; " + blockInWorld.y + "; " + blockInWorld.z + ") " + + "do not belong to a loaded chunk" + ); + + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk); + chunk.setBlock(blockInChunk, block, notify); + } + public Collection getChunks() { return chunks; } @@ -132,4 +167,16 @@ public class WorldData { return block.getCollisionModel(); } + public Collection getListeners() { + return listeners; + } + + public void addListener(WorldDataListener e) { + listeners.add(e); + } + + public void removeListener(WorldDataListener o) { + listeners.remove(o); + } + } diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java b/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java new file mode 100644 index 0000000..12b3b9f --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldDataListener.java @@ -0,0 +1,34 @@ +package ru.windcorp.progressia.common.world; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; + +public interface WorldDataListener { + + /** + * Invoked when a new {@link ChunkData} instance is created. This method should be used to add + * {@link ChunkDataListener}s to a new chunk. When listeners are added with this method, + * their {@link ChunkDataListener#onChunkLoaded(ChunkData) onChunkLoaded} methods will be invoked. + * @param world the world instance + * @param chunk the {@linkplain Coordinates#chunk coordinates of chunk} of the chunk about to load + * @param chunkListenerSink a sink for listeners. All listeners passed to its + * {@link Consumer#accept(Object) accept} method will be added to the chunk. + */ + default void getChunkListeners(WorldData world, Vec3i chunk, Consumer chunkListenerSink) {} + + /** + * Invoked whenever a {@link Chunk} has been loaded. + * @param world the world instance + * @param chunk the chunk that has loaded + */ + default void onChunkLoaded(WorldData world, ChunkData chunk) {} + + /** + * Invoked whenever a {@link Chunk} is about to be unloaded. + * @param world the world instance + * @param chunk the chunk that is going to be unloaded + */ + default void beforeChunkUnloaded(WorldData world, ChunkData chunk) {} + +} diff --git a/src/main/java/ru/windcorp/progressia/server/ServerState.java b/src/main/java/ru/windcorp/progressia/server/ServerState.java index d8071f6..aa19807 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerState.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerState.java @@ -16,6 +16,7 @@ public class ServerState { public static void startServer() { Server server = new Server(new WorldData()); + server.getWorld().getData().tmp_generate(); setInstance(server); server.start(); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java index 2aae605..10e0db8 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -5,8 +5,12 @@ import java.util.HashMap; import java.util.Map; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.WorldDataListener; +import ru.windcorp.progressia.server.world.block.BlockLogic; public class WorldLogic { @@ -17,9 +21,17 @@ public class WorldLogic { public WorldLogic(WorldData data) { this.data = data; - for (ChunkData chunkData : data.getChunks()) { - chunks.put(chunkData, new ChunkLogic(this, chunkData)); - } + data.addListener(new WorldDataListener() { + @Override + public void onChunkLoaded(WorldData world, ChunkData chunk) { + chunks.put(chunk, new ChunkLogic(WorldLogic.this, chunk)); + } + + @Override + public void beforeChunkUnloaded(WorldData world, ChunkData chunk) { + chunks.remove(chunk); + } + }); } public WorldData getData() { @@ -34,6 +46,25 @@ public class WorldLogic { return chunks.get(getData().getChunk(pos)); } + public ChunkLogic getChunkByBlock(Vec3i blockInWorld) { + Vec3i chunkPos = Vectors.grab3i(); + Coordinates.convertInWorldToChunk(blockInWorld, chunkPos); + ChunkLogic result = getChunk(chunkPos); + Vectors.release(chunkPos); + return result; + } + + public BlockLogic getBlock(Vec3i blockInWorld) { + ChunkLogic chunk = getChunkByBlock(blockInWorld); + if (chunk == null) return null; + + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk); + BlockLogic result = chunk.getBlock(blockInChunk); + + return result; + } + public Collection getChunks() { return chunks.values(); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/UpdatableBlock.java b/src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java similarity index 100% rename from src/main/java/ru/windcorp/progressia/server/world/block/UpdatableBlock.java rename to src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java From ce2d4297dd83216b496a5a0efeffc7f24abe7872 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Sat, 21 Nov 2020 23:51:14 +0300 Subject: [PATCH 24/24] Fixed compilation problems and added MultiLOC (MultiLOC is MultiLowOverheadCache) --- .../progressia/common/util/MultiLOC.java | 25 +++++++++++++++++++ .../world/ImplementedChangeTracker.java | 2 +- .../server/world/block/UpdateableBlock.java | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/common/util/MultiLOC.java diff --git a/src/main/java/ru/windcorp/progressia/common/util/MultiLOC.java b/src/main/java/ru/windcorp/progressia/common/util/MultiLOC.java new file mode 100644 index 0000000..b9e67d9 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/MultiLOC.java @@ -0,0 +1,25 @@ +package ru.windcorp.progressia.common.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class MultiLOC { + + private final Map, LowOverheadCache> caches = new HashMap<>(); + + public MultiLOC addClass(Class clazz, Supplier generator) { + caches.put(clazz, new LowOverheadCache<>(generator)); + return this; + } + + public T grab(Class clazz) { + return clazz.cast(caches.get(clazz).grab()); + } + + @SuppressWarnings("unchecked") + public void release(Object obj) { + ((LowOverheadCache) caches.get(obj.getClass())).release(obj); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java index c681e01..3cb6a8b 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java @@ -53,7 +53,7 @@ public class ImplementedChangeTracker implements Changer { Vec3i blockInChunk = Vectors.grab3i(); Coordinates.convertInWorldToInChunk(position, blockInChunk); - world.getChunkByBlock(position).setBlock(blockInChunk, block); + world.getChunkByBlock(position).setBlock(blockInChunk, block, true); Vectors.release(blockInChunk); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java b/src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java index b76f6da..406f4c9 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/UpdateableBlock.java @@ -2,7 +2,7 @@ package ru.windcorp.progressia.server.world.block; import ru.windcorp.progressia.server.world.Changer; -public interface UpdatableBlock { +public interface UpdateableBlock { void update(BlockTickContext context, Changer changer);