Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,13 @@ blacklist.json
terasology.jfr
local.properties
*.hprof

### BROKK'S CONFIGURATION ###
.brokk/**
/.brokk/workspace.properties
/.brokk/sessions/
/.brokk/dependencies/
/.brokk/history.zip
!AGENTS.md
!.brokk/style.md
!.brokk/project.properties
52 changes: 52 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Terasology Coding Style Guide

This guide captures the specific coding patterns and Gradle conventions found in the Terasology codebase, focusing on modern Gradle Kotlin DSL (KTS) usage and engine-specific architectures.

## 1. Build System & Gradle (KTS)

The project uses Gradle Kotlin DSL with a centralized `build-logic` composite build.

### Dependency Management
- **Version Catalogs**: Use the `libs` catalog in `settings.gradle.kts` for common dependencies.
- **Constraints**: Use `constraints { ... }` blocks to force specific transitive dependency versions (e.g., forcing a newer `bytebuddy` for Java 17 compatibility).
- **Dependency Substitution**: Use `resolutionStrategy.dependencySubstitution` to swap remote module dependencies with local project sources when available.
- **Platform BOMs**: Use `platform()` for grouping related dependencies (e.g., LWJGL).

### Task Configuration
- **Lazy Registration**: Favor `tasks.register<Type>("name")` over `tasks.create`.
- **Property Delegation**: Use `val propertyName by extra("value")` for sharing variables across subprojects.
- **Inputs/Outputs**: Always define `outputs.dir` or `outputs.file` for custom extraction/copy tasks to support Gradle's up-to-date checks and automatic `clean` task generation.
- **Duplication Strategy**: Explicitly set `duplicatesStrategy = DuplicatesStrategy.EXCLUDE` when merging resources or classes from multiple source sets.

### Java Toolchains & Compatibility
- **JVM Toolchain**: Strictly target Java 17 using `jvmToolchain(17)`.
- **Validation**: Include runtime checks in build scripts to warn users if the `JavaVersion.current()` deviates from the expected version.

## 2. Java & Engine Architecture

### Dependency Injection & Context
- **The `@In` Annotation**: Use the `@In` annotation for automatic service injection within NUI screens and engine components.
- **Context Registry**: Use `context.get(Class<T>)` to retrieve engine services and `CoreRegistry.put()` for global accessibility when necessary.

### Resource & Asset Handling
- **ResourceUrn**: Always use `ResourceUrn` for identifying assets (e.g., `engine:universeSetupScreen`).
- **Asset Templates**: Store static configuration templates in the `/templates` directory and use custom tasks like `CopyButNeverOverwrite` to initialize them into the project root.

### UI Development (NUI)
- **Binding Pattern**: Use `Binding<T>` and `ReadOnlyBinding<T>` to link UI widgets (like `UIText` or `UIDropdown`) to underlying configuration data.
- **Widget Subscription**: Use `WidgetUtil.trySubscribe(this, "widgetId", handler)` for cleaner event handling.
- **Wait Popups**: Long-running operations (like world preview generation) must be wrapped in a `WaitPopup` using `Callable` operations to keep the UI responsive.

## 3. Automation & CI (Jenkins)

- **Declarative Pipelines**: Use Jenkins Declarative Pipeline syntax with `post { always { ... } }` for test result aggregation.
- **Static Analysis Integration**: Build stages should explicitly trigger `recordIssues` for Checkstyle, PMD, and SpotBugs.
- **Flaky Test Management**: Segregate tests using JUnit 5 tags:
- `unitTest`: Fast, excludes `MteTest`/`TteTest`.
- `integrationTest`: Slow, includes `MteTest`/`TteTest`, excludes `flaky`.
- `integrationTestFlaky`: Only runs tests tagged with both integration tags and `flaky`.

## 4. Metadata & Versioning

- **Module Metadata**: Engine and module versions should be mastered in `module.txt` (JSON format) and read into the build system via `JsonSlurper`.
- **Version Info**: The build must generate a `versionInfo.properties` file containing Git hashes, build numbers, and timestamps to be bundled within the JAR for runtime diagnostics.
25 changes: 16 additions & 9 deletions facades/PC/src/main/java/org/terasology/engine/Terasology.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,18 @@

@Override
public Integer call() throws IOException {
// IMPORTANT: Logging must be initialized BEFORE any class with a static Logger field
// is loaded. Classes like PathManager have static loggers that trigger Logback
// initialization when the class is loaded. If Logback initializes before
// LoggingContext.initialize() sets the logFileFolder system property,
// it creates a "logFileFolder_IS_UNDEFINED" directory.
//
// Therefore: compute log path directly from homeDir, then call setupLogging(),
// THEN call handleLaunchArguments() which loads PathManager.
Path logPath = (homeDir != null ? homeDir : Paths.get(".")).resolve("logs");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log path wrong when homeDir is null

High Severity

When homeDir is null (the default for most users who don't pass --homedir), the fallback Paths.get(".") directs logs to the current working directory. Previously, setupLogging obtained the log path from PathManager.getInstance().getLogPath() after handleLaunchArguments() had called useDefaultHomePath(), which resolves to a platform-specific directory (e.g., ~/.local/share/terasology/logs on Linux). The new code cannot replicate that logic without loading PathManager, so it silently writes logs to the wrong location for the common no---homedir case.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good bot! I had wondered about this. Would you recommend a fix or is this a tricky case?

setupLogging(logPath);

handleLaunchArguments();
setupLogging();

SplashScreen splashScreen;
if (splashEnabled) {
Expand Down Expand Up @@ -257,13 +267,8 @@
return (newTitle + " " + fileNumber);
}

private static void setupLogging() {
Path path = PathManager.getInstance().getLogPath();
if (path == null) {
path = Paths.get("logs");
}

LoggingContext.initialize(path);
private static void setupLogging(Path logPath) {
LoggingContext.initialize(logPath);
}

private void handleLaunchArguments() throws IOException {
Expand All @@ -275,9 +280,11 @@
}

if (homeDir != null) {
logger.info("homeDir is {}", homeDir);
// Use stdout here since this is pre-initialization diagnostic output.
// Logger is available but using println keeps the initialization sequence clear.
System.out.println("homeDir is " + homeDir);

Check warning on line 285 in facades/PC/src/main/java/org/terasology/engine/Terasology.java

View check run for this annotation

Terasology Jenkins.io / PMD

SystemPrintln

HIGH: Usage of System.out/err.
Raw output
References to System.(out|err).print are usually intended for debugging purposes and can remain in the codebase even in production code. By using a logger one can enable/disable this behaviour at will (and by priority) and avoid clogging the Standard out log. <pre> <code> class Foo{ Logger log = Logger.getLogger(Foo.class.getName()); public void testA () { System.out.println(&quot;Entering test&quot;); // Better use this log.fine(&quot;Entering test&quot;); } } </code> </pre> <a href="https://docs.pmd-code.org/pmd-doc-7.7.0/pmd_rules_java_bestpractices.html#systemprintln"> See PMD documentation. </a>
PathManager.getInstance().useOverrideHomePath(homeDir);
// TODO: what is this?

Check warning on line 287 in facades/PC/src/main/java/org/terasology/engine/Terasology.java

View check run for this annotation

Terasology Jenkins.io / Open Tasks Scanner

TODO

NORMAL: what is this?
// PathManager.getInstance().chooseHomePathManually();
} else {
PathManager.getInstance().useDefaultHomePath();
Expand Down
Loading