Merge branch 'develop' into java-18-disable-securitymanager

This commit is contained in:
jdrueckert 2023-07-18 00:06:28 +02:00 committed by GitHub
commit 9cca631e77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 343 additions and 2809 deletions

1
.gitignore vendored
View file

@ -16,7 +16,6 @@
extensions
/facades/*
!/facades/PC
!/facades/TeraEd
!/facades/subprojects.gradle
/modules/*
!/modules/subprojects.gradle

6
.idea/kotlinc.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.6.21" />
</component>
</project>

View file

@ -1,27 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TeraEd (rendering editor)" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.terasology.editor.TeraEd" />
<module name="Terasology.facades.TeraEd.main" />
<option name="PROGRAM_PARAMETERS" value="-homedir" />
<shortenClasspath name="MANIFEST" />
<option name="VM_PARAMETERS" value="-Xms256m -Xmx1536m" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.terasology.editor.TeraEd.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="" />
<option name="TRANSPORT" value="0" />
<option name="LOCAL" value="true" />
</RunnerSettings>
<RunnerSettings RunnerId="Run" />
<ConfigurationWrapper RunnerId="Debug" />
<ConfigurationWrapper RunnerId="Run" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="fetchModuleDependencies" externalProjectPath="$PROJECT_DIR$/modules" vmOptions="" scriptParameters="" />
</method>
</configuration>
</component>

4
Jenkinsfile vendored
View file

@ -12,7 +12,9 @@ properties([
// that can't simply be turned off
copyArtifactPermission('*'),
// Flag for Jenkins to discard attached artifacts after x builds
buildDiscarder(logRotator(artifactNumToKeepStr: artifactBuildsToKeep))
buildDiscarder(logRotator(artifactNumToKeepStr: artifactBuildsToKeep)),
// configure Jenkins to abort a build if a new one is triggered for the same branch
disableConcurrentBuilds(abortPrevious: true)
])
/**

View file

@ -1,7 +1,7 @@
class facade {
def excludedItems = ["PC", "TeraEd"]
def excludedItems = ["PC"]
def getGithubDefaultHome(Properties properties) {
return properties.alternativeGithubHome ?: "MovingBlocks"

View file

@ -0,0 +1,6 @@
{
"ListOfObject" : {
"shortName" : "Test",
"elements" : [ ]
}
}

View file

@ -142,6 +142,14 @@ public void testPrefabWithListOfMappedContainers() {
assertEquals("returnHome", mappedContainer.elements.get(1).id);
}
@Test
public void testPrefabWithEmptyListOfMappedContainers() {
Prefab prefab = prefabManager.getPrefab("unittest:withEmptyListContainer");
ListOfObjectComponent mappedContainer = prefab.getComponent(ListOfObjectComponent.class);
assertEquals(0, mappedContainer.elements.size());
}
@Test
public void testPrefabWithListOfEnums() {
Prefab prefab = prefabManager.getPrefab("unittest:withListEnumContainer");

View file

@ -0,0 +1,34 @@
// Copyright 2023 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.network;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.gestalt.entitysystem.component.Component;
import java.util.HashMap;
import java.util.Map;
/**
* PingStockComponent stock the ping information of one user.
* <p>
* Might be used to stock ping information and display it in future.
*/
public final class PingComponent implements Component<PingComponent> {
@Replicate
private Map<EntityRef, Long> pings = new HashMap<>();
public void setValues(Map<EntityRef, Long> values) {
pings.clear();
pings.putAll(values);
}
public Map<EntityRef, Long> getValues() {
return new HashMap<>(pings);
}
@Override
public void copyFrom(PingComponent other) {
this.setValues(other.getValues());
}
}

View file

@ -1,50 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.network;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.gestalt.entitysystem.component.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* PingStockComponent stock the ping information of one user.
* <p>
* Might be used to stock ping information and display it in future.
*/
public final class PingStockComponent implements Component<PingStockComponent> {
// TODO Map<EntityRef,Long> is not supported for replication (no type handler),
// therefore keys and values are replicated via lists.
// Not the best solution for performance but for <100 players and low update rates it should do the job
@Replicate
private List<EntityRef> pingKeys = new ArrayList<>();
@Replicate
private List<Long> pingValues = new ArrayList<>();
public void setValues(Map<EntityRef, Long> values) {
pingKeys.clear();
pingValues.clear();
for (Map.Entry<EntityRef, Long> entry : values.entrySet()) {
pingKeys.add(entry.getKey());
pingValues.add(entry.getValue());
}
}
public Map<EntityRef, Long> getValues() {
Map<EntityRef, Long> returnValues = new HashMap<>();
for (int i = 0; i < pingKeys.size(); i++) {
returnValues.put(pingKeys.get(i), pingValues.get(i));
}
return returnValues;
}
@Override
public void copyFrom(PingStockComponent other) {
this.setValues(other.getValues());
}
}

View file

@ -1,14 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.network;
import org.terasology.gestalt.entitysystem.component.EmptyComponent;
/**
* PingSubscriberComponent, only on the server system, will be added to a client entity when this client subscribe.
* Server will only send ping information to the clients subscribed.
* <p>
* It can be used to stock the ping information of users in future.
*/
public class PingSubscriberComponent extends EmptyComponent<PingSubscriberComponent> {
}

View file

@ -1,4 +1,4 @@
// Copyright 2021 The Terasology Foundation
// Copyright 2023 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.network;
@ -8,7 +8,6 @@
import org.terasology.engine.entitySystem.systems.RegisterMode;
import org.terasology.engine.entitySystem.systems.RegisterSystem;
import org.terasology.engine.entitySystem.systems.UpdateSubscriberSystem;
import org.terasology.engine.logic.players.LocalPlayer;
import org.terasology.engine.network.events.DisconnectedEvent;
import org.terasology.engine.network.events.PingFromClientEvent;
import org.terasology.engine.network.events.PingFromServerEvent;
@ -25,18 +24,21 @@
/**
* This system implement the server ping to clients on need base.
* It runs on the server, pings to all clients who subscribe this function.
*
* @see PingFromServerEvent
* @see PingFromClientEvent
* @see SubscribePingEvent
* @see UnSubscribePingEvent
*/
@RegisterSystem(RegisterMode.AUTHORITY)
public class ServerPingSystem extends BaseComponentSystem implements UpdateSubscriberSystem {
/** The interval in which pings are sent, in milliseconds. */
private static final long PING_PERIOD = 200;
@In
private EntityManager entityManager;
@In
private LocalPlayer localPlayer;
private Map<EntityRef, Instant> startMap = new HashMap<>();
private Map<EntityRef, Instant> endMap = new HashMap<>();
@ -52,54 +54,77 @@ public void initialise() {
@Override
public void update(float delta) {
long time = Duration.between(lastPingTime, Instant.now()).toMillis();
Instant now = Instant.now();
long time = Duration.between(lastPingTime, now).toMillis();
if (time > PING_PERIOD) {
// Server ping to all clients only if there are clients who subscribe
if (entityManager.getCountOfEntitiesWith(PingSubscriberComponent.class) != 0) {
Iterable<EntityRef> clients = entityManager.getEntitiesWith(ClientComponent.class);
for (EntityRef client : clients) {
if (client.equals(localPlayer.getClientEntity())) {
continue;
}
// send ping only if client replied the last ping
Instant lastPingFromClient = endMap.get(client);
Instant lastPingToClient = startMap.get(client);
// Only happens when server doesn't receive ping back yet
if (lastPingFromClient != null && lastPingToClient != null && lastPingFromClient.isBefore(lastPingToClient)) {
continue;
}
Instant start = Instant.now();
startMap.put(client, start);
client.send(new PingFromServerEvent());
}
// only collect ping information if anybody is interested
if (entityManager.getCountOfEntitiesWith(PingComponent.class) > 0) {
startPings();
updateSubscribers();
} else {
clear();
}
lastPingTime = now;
}
}
//update ping data for all clients
for (EntityRef client : entityManager.getEntitiesWith(PingSubscriberComponent.class)) {
PingStockComponent pingStockComponent;
if (!client.hasComponent(PingStockComponent.class)) {
pingStockComponent = new PingStockComponent();
} else {
pingStockComponent = client.getComponent(PingStockComponent.class);
}
if (localPlayer != null && localPlayer.getClientEntity() != null) {
pingMap.put(localPlayer.getClientEntity(), new Long(5));
}
pingStockComponent.setValues(pingMap);
client.addOrSaveComponent(pingStockComponent);
}
/**
* Clear internal maps, for instance, when there are no more subscribers.
*/
private void clear() {
startMap.clear();
endMap.clear();
pingMap.clear();
}
lastPingTime = Instant.now();
/**
* Send a ping signal ({@link PingFromServerEvent}) from the server to all
* clients.
*
* Any entity with a {@link PingComponent} is considered as subscriber.
*
* Clients are supposed to answer with {@link PingFromClientEvent} to confirm
* the ping.
*/
private void startPings() {
for (EntityRef client : entityManager.getEntitiesWith(ClientComponent.class)) {
sendPingToClient(client);
}
}
/**
* Send a ping signal to the client.
*/
private void sendPingToClient(EntityRef client) {
Instant lastPingFromClient = endMap.get(client);
Instant lastPingToClient = startMap.get(client);
// Send ping only if the client has replied to the last ping. This happens when
// there is still a ping in-flight, that is, the server hasn't received an answer
// from this client yet.
if (lastPingFromClient != null && lastPingToClient != null
&& lastPingFromClient.isBefore(lastPingToClient)) {
return;
}
startMap.put(client, Instant.now());
client.send(new PingFromServerEvent());
}
/**
* Update the ping stock ({@link PingComponent}) on all subscribers
*/
private void updateSubscribers() {
for (EntityRef client : entityManager.getEntitiesWith(PingComponent.class)) {
client.updateComponent(PingComponent.class, pingComponent -> {
pingComponent.setValues(pingMap);
return pingComponent;
});
}
}
@ReceiveEvent(components = ClientComponent.class)
public void onPingFromClient(PingFromClientEvent event, EntityRef entity) {
Instant end = Instant.now();
endMap.put(entity, end);
endMap.put(entity, Instant.now());
updatePing(entity);
}
@ -119,19 +144,11 @@ public void onDisconnected(DisconnectedEvent event, EntityRef entity) {
@ReceiveEvent(components = ClientComponent.class)
public void onSubscribePing(SubscribePingEvent event, EntityRef entity) {
entity.addOrSaveComponent(new PingSubscriberComponent());
entity.addOrSaveComponent(new PingComponent());
}
@ReceiveEvent(components = ClientComponent.class)
public void onUnSubscribePing(UnSubscribePingEvent event, EntityRef entity) {
entity.removeComponent(PingSubscriberComponent.class);
entity.removeComponent(PingStockComponent.class);
//if there is no pingSubscriber, then clean the map
if (entityManager.getCountOfEntitiesWith(PingSubscriberComponent.class) == 0) {
startMap.clear();
endMap.clear();
pingMap.clear();
}
entity.removeComponent(PingComponent.class);
}
}

View file

@ -1,34 +1,33 @@
// Copyright 2021 The Terasology Foundation
// Copyright 2023 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.rendering.nui.layers.ingame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.engine.entitySystem.entity.EntityManager;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.logic.afk.AfkComponent;
import org.terasology.engine.logic.players.LocalPlayer;
import org.terasology.engine.logic.players.PlayerUtil;
import org.terasology.engine.network.ClientComponent;
import org.terasology.engine.network.PingStockComponent;
import org.terasology.engine.network.PingComponent;
import org.terasology.engine.network.events.SubscribePingEvent;
import org.terasology.engine.network.events.UnSubscribePingEvent;
import org.terasology.engine.registry.In;
import org.terasology.engine.rendering.nui.CoreScreenLayer;
import org.terasology.nui.Color;
import org.terasology.nui.FontColor;
import org.terasology.nui.databinding.ReadOnlyBinding;
import org.terasology.nui.widgets.UIText;
import org.terasology.engine.registry.In;
import org.terasology.engine.rendering.nui.CoreScreenLayer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Overlay that lists all players that are currently online.
* Overlay that lists all players that are currently online and their pings.
*/
public class OnlinePlayersOverlay extends CoreScreenLayer {
private static final Logger logger = LoggerFactory.getLogger(OnlinePlayersOverlay.class);
private UIText text;
@In
@ -43,69 +42,65 @@ public void initialise() {
text.bindText(new ReadOnlyBinding<String>() {
@Override
public String get() {
logger.info("localPlayer is: {}", localPlayer);
PingStockComponent pingStockComp = localPlayer.getClientEntity().getComponent(PingStockComponent.class);
if (pingStockComp == null) {
String playerListText = determinePlayerListText();
return playerListText;
} else {
String playerAndPing = determinePlayerAndPing(pingStockComp);
return playerAndPing;
}
PingComponent pingComponent = localPlayer.getClientEntity().getComponent(PingComponent.class);
return determinePlayerList(getPingMap(pingComponent));
}
});
}
private String determinePlayerListText() {
Iterable<EntityRef> allClients = entityManager.getEntitiesWith(ClientComponent.class);
StringBuilder sb = new StringBuilder();
boolean first = true;
for (EntityRef clientEntity : allClients) {
if (!first) {
sb.append("\n");
/**
* Assemble a map from connected players (or clients) to their ping.
*
* If the ping component is null, the connected clients are determined by looking at entities with the {@link ClientComponent}.
* In this case, the ping values are {@code null}.
*
* @param pingComponent component with information on connected players, or {@code null} if not present
* @return a mapping from connected clients to their respective ping (or {@code null} if the ping cannot be determined)
*/
private Map<EntityRef, Long> getPingMap(PingComponent pingComponent) {
//TODO: There's a noticeable delay when opening the overlay before the first ping comes in and all players are shown.
// We could either try to match the entity refs here, or pre-fill the component sooner in the ServerPingSystem.
if (pingComponent != null) {
return pingComponent.getValues();
} else {
Map<EntityRef, Long> pings = new HashMap<>();
for (EntityRef client : entityManager.getEntitiesWith(ClientComponent.class)) {
pings.put(client, null);
}
ClientComponent clientComp = clientEntity.getComponent(ClientComponent.class);
sb.append(PlayerUtil.getColoredPlayerName(clientComp.clientInfo));
first = false;
return pings;
}
return sb.toString();
}
private String determinePlayerAndPing(PingStockComponent pingStockComponent) {
Map<EntityRef, Long> pingMap = pingStockComponent.getValues();
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry<EntityRef, Long> entry : pingMap.entrySet()) {
EntityRef clientEntity = entry.getKey();
if (clientEntity == null || clientEntity.getComponent(ClientComponent.class) == null) {
logger.warn("OnlinePlayersOverlay skipping a null client entity or component");
continue;
}
if (!first) {
sb.append("\n");
}
ClientComponent clientComp = clientEntity.getComponent(ClientComponent.class);
AfkComponent afkComponent = clientEntity.getComponent(AfkComponent.class);
if (afkComponent != null) {
if (afkComponent.afk) {
sb.append(FontColor.getColored("[AFK]", Color.red));
sb.append(" ");
}
}
sb.append(PlayerUtil.getColoredPlayerName(clientComp.clientInfo));
sb.append(" ");
Long pingValue = pingMap.get(clientEntity);
if (pingValue == null) {
sb.append("-");
} else {
sb.append(pingValue.toString());
sb.append("ms");
}
first = false;
/**
* Create multi-line string, containing one line per connected player.
*/
private String determinePlayerList(Map<EntityRef, Long> pings) {
List<String> lines = new ArrayList<>();
for (Map.Entry<EntityRef, Long> entry : pings.entrySet()) {
lines.add(determinePlayerLine(entry.getKey(), entry.getValue()));
}
return sb.toString();
return String.join("\n", lines);
}
/**
* Create a single-line string with the player name and their ping.
*
* <pre>
* [AFK] Player4612 42ms
* -------- -------------------------------- --------
* 8 32 8
* </pre>
*/
private String determinePlayerLine(EntityRef client, Long ping) {
ClientComponent clientComp = client.getComponent(ClientComponent.class);
AfkComponent afkComponent = client.getComponent(AfkComponent.class);
String prefix = (afkComponent != null && afkComponent.afk) ? FontColor.getColored("[AFK]", Color.red) : "";
String displayName = PlayerUtil.getColoredPlayerName(clientComp.clientInfo);
String displayPing = (ping != null) ? ping + "ms" : FontColor.getColored("---", Color.grey);
//TODO: the formatting does not work well since we're not using a mono-spaced font. we should investigate whether we can use
// a different UI element or at least a monospaced font to align prefix, player name, and ping better.
return String.format("%-8s%-32s%8s", prefix, displayName, displayPing);
}
@Override

View file

@ -1,377 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.rendering.nui.layers.mainMenu;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.engine.config.Config;
import org.terasology.engine.context.Context;
import org.terasology.engine.context.internal.ContextImpl;
import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.core.bootstrap.EnvironmentSwitchHandler;
import org.terasology.engine.core.module.ModuleManager;
import org.terasology.engine.entitySystem.metadata.ComponentLibrary;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.registry.In;
import org.terasology.engine.rendering.assets.texture.Texture;
import org.terasology.engine.rendering.assets.texture.TextureData;
import org.terasology.engine.rendering.nui.CoreScreenLayer;
import org.terasology.engine.rendering.nui.NUIManager;
import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems;
import org.terasology.engine.rendering.nui.layers.mainMenu.preview.FacetLayerPreview;
import org.terasology.engine.rendering.nui.layers.mainMenu.preview.PreviewGenerator;
import org.terasology.engine.utilities.Assets;
import org.terasology.engine.world.generator.WorldConfigurator;
import org.terasology.engine.world.generator.WorldGenerator;
import org.terasology.engine.world.generator.internal.WorldGeneratorManager;
import org.terasology.engine.world.generator.plugin.TempWorldGeneratorPluginLibrary;
import org.terasology.engine.world.generator.plugin.WorldGeneratorPluginLibrary;
import org.terasology.engine.world.zones.Zone;
import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.gestalt.assets.module.ModuleAwareAssetTypeManager;
import org.terasology.gestalt.entitysystem.component.Component;
import org.terasology.gestalt.module.ModuleEnvironment;
import org.terasology.gestalt.module.dependencyresolution.DependencyResolver;
import org.terasology.gestalt.module.dependencyresolution.ResolutionResult;
import org.terasology.gestalt.module.exceptions.UnresolvedDependencyException;
import org.terasology.math.TeraMath;
import org.terasology.nui.WidgetUtil;
import org.terasology.nui.databinding.Binding;
import org.terasology.nui.layouts.PropertyLayout;
import org.terasology.nui.properties.OneOfProviderFactory;
import org.terasology.nui.properties.Property;
import org.terasology.nui.properties.PropertyOrdering;
import org.terasology.nui.properties.PropertyProvider;
import org.terasology.nui.widgets.UIButton;
import org.terasology.nui.widgets.UIDropdown;
import org.terasology.nui.widgets.UIImage;
import org.terasology.nui.widgets.UISlider;
import org.terasology.nui.widgets.UIText;
import org.terasology.reflection.metadata.FieldMetadata;
import org.terasology.reflection.reflect.ReflectFactory;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
/**
* Shows a preview of the generated world and provides some
* configuration options to tweak the generation process.
*/
public class PreviewWorldScreen extends CoreScreenLayer {
public static final ResourceUrn ASSET_URI = new ResourceUrn("engine:previewWorldScreen");
private static final Logger logger = LoggerFactory.getLogger(PreviewWorldScreen.class);
@In
private ModuleManager moduleManager;
@In
private ModuleAwareAssetTypeManager assetTypeManager;
@In
private WorldGeneratorManager worldGeneratorManager;
@In
private Config config;
@In
private Context context;
private WorldGenerator worldGenerator;
private UIImage previewImage;
private UISlider zoomSlider;
private UIDropdown<Zone> zoneSelector;
private UIButton applyButton;
private UIText seed;
private PreviewGenerator previewGen;
private Context subContext;
private ModuleEnvironment environment;
private Texture texture;
private boolean triggerUpdate;
private String targetZone = "Surface";
public PreviewWorldScreen() {
}
public void setEnvironment() throws Exception {
// TODO: pass world gen and module list directly rather than using the config
SimpleUri worldGenUri = config.getWorldGeneration().getDefaultGenerator();
DependencyResolver resolver = new DependencyResolver(moduleManager.getRegistry());
ResolutionResult result = resolver.resolve(config.getDefaultModSelection().listModules());
if (result.isSuccess()) {
subContext = new ContextImpl(context);
CoreRegistry.setContext(subContext);
environment = moduleManager.loadEnvironment(result.getModules(), false);
subContext.put(WorldGeneratorPluginLibrary.class, new TempWorldGeneratorPluginLibrary(environment, subContext));
EnvironmentSwitchHandler environmentSwitchHandler = context.get(EnvironmentSwitchHandler.class);
environmentSwitchHandler.handleSwitchToPreviewEnvironment(subContext, environment);
genTexture();
worldGenerator = WorldGeneratorManager.createWorldGenerator(worldGenUri, subContext, environment);
worldGenerator.setWorldSeed(seed.getText());
List<Zone> previewZones = Lists.newArrayList(worldGenerator.getZones())
.stream()
.filter(z -> !z.getPreviewLayers().isEmpty())
.collect(Collectors.toList());
if (previewZones.isEmpty()) {
zoneSelector.setVisible(false);
previewGen = new FacetLayerPreview(environment, worldGenerator);
} else {
zoneSelector.setVisible(true);
zoneSelector.setOptions(previewZones);
zoneSelector.setSelection(previewZones.get(0));
}
configureProperties();
} else {
throw new UnresolvedDependencyException("Unable to resolve dependencies for " + worldGenUri);
}
}
private void genTexture() {
int imgWidth = 384;
int imgHeight = 384;
ByteBuffer buffer = ByteBuffer.allocateDirect(imgWidth * imgHeight * Integer.BYTES);
ByteBuffer[] data = new ByteBuffer[]{buffer};
ResourceUrn uri = new ResourceUrn("engine:terrainPreview");
TextureData texData = new TextureData(imgWidth, imgHeight, data, Texture.WrapMode.CLAMP, Texture.FilterMode.LINEAR);
texture = Assets.generateAsset(uri, texData, Texture.class);
previewImage = find("preview", UIImage.class);
previewImage.setImage(texture);
}
@Override
public void update(float delta) {
super.update(delta);
if (triggerUpdate) {
updatePreview();
triggerUpdate = false;
}
}
private void configureProperties() {
PropertyLayout propLayout = find("properties", PropertyLayout.class);
propLayout.setOrdering(PropertyOrdering.byLabel());
propLayout.clear();
WorldConfigurator worldConfig = worldGenerator.getConfigurator();
Map<String, Component> params = worldConfig.getProperties();
for (String key : params.keySet()) {
Class<? extends Component> clazz = params.get(key).getClass();
Component comp = config.getModuleConfig(worldGenerator.getUri(), key, clazz);
if (comp != null) {
worldConfig.setProperty(key, comp); // use the data from the config instead of defaults
}
}
ComponentLibrary compLib = subContext.get(ComponentLibrary.class);
for (String label : params.keySet()) {
PropertyProvider provider = new PropertyProvider(context.get(ReflectFactory.class), context.get(OneOfProviderFactory.class)) {
@Override
protected <T> Binding<T> createTextBinding(Object target, FieldMetadata<Object, T> fieldMetadata) {
return new WorldConfigBinding<>(worldConfig, label, compLib, fieldMetadata);
}
@Override
protected Binding<Float> createFloatBinding(Object target, FieldMetadata<Object, ?> fieldMetadata) {
return new WorldConfigNumberBinding(worldConfig, label, compLib, fieldMetadata);
}
};
Component target = params.get(label);
List<Property<?, ?>> properties = provider.createProperties(target);
propLayout.addProperties(label, properties);
}
}
private void resetEnvironment() {
CoreRegistry.setContext(context);
if (environment != null) {
EnvironmentSwitchHandler environmentSwitchHandler = context.get(EnvironmentSwitchHandler.class);
environmentSwitchHandler.handleSwitchBackFromPreviewEnvironment(subContext);
environment.close();
environment = null;
}
previewGen.close();
WorldConfigurator worldConfig = worldGenerator.getConfigurator();
Map<String, Component> params = worldConfig.getProperties();
if (params != null) {
config.setModuleConfigs(worldGenerator.getUri(), params);
}
}
@Override
public void initialise() {
setAnimationSystem(MenuAnimationSystems.createDefaultSwipeAnimation());
zoomSlider = find("zoomSlider", UISlider.class);
if (zoomSlider != null) {
zoomSlider.setValue(2f);
}
seed = find("seed", UIText.class);
zoneSelector = find("zoneSelector", UIDropdown.class);
applyButton = find("apply", UIButton.class);
if (applyButton != null) {
applyButton.subscribe(widget -> updatePreview());
}
WidgetUtil.trySubscribe(this, "close", w -> {
resetEnvironment();
triggerBackAnimation();
});
}
@Override
public boolean isLowerLayerVisible() {
return false;
}
public void bindSeed(Binding<String> binding) {
if (seed == null) {
// TODO: call initialize through NUIManager instead of onOpened()
seed = find("seed", UIText.class);
}
seed.bindText(binding);
}
private void updatePreview() {
final NUIManager manager = context.get(NUIManager.class);
final WaitPopup<TextureData> popup = manager.pushScreen(WaitPopup.ASSET_URI, WaitPopup.class);
popup.setMessage("Updating Preview", "Please wait ...");
ProgressListener progressListener = progress ->
popup.setMessage("Updating Preview", String.format("Please wait ... %d%%", (int) (progress * 100f)));
Callable<TextureData> operation = () -> {
if (seed != null) {
worldGenerator.setWorldSeed(seed.getText());
}
int zoom = TeraMath.floorToInt(zoomSlider.getValue());
TextureData data = texture.getData();
if (zoneSelector.isVisible()) {
previewGen = zoneSelector.getSelection().preview(worldGenerator);
}
previewGen.render(data, zoom, progressListener);
return data;
};
popup.onSuccess(texture::reload);
popup.startOperation(operation, true);
}
/**
* Updates a world configurator through setProperty() whenever Binding#set() is called.
*/
private static class WorldConfigBinding<T> implements Binding<T> {
private final String label;
private final WorldConfigurator worldConfig;
private final FieldMetadata<Object, T> fieldMetadata;
private final ComponentLibrary compLib;
protected WorldConfigBinding(WorldConfigurator config, String label, ComponentLibrary compLib, FieldMetadata<Object, T> fieldMetadata) {
this.worldConfig = config;
this.label = label;
this.compLib = compLib;
this.fieldMetadata = fieldMetadata;
}
@Override
public T get() {
Component comp = worldConfig.getProperties().get(label);
return fieldMetadata.getValue(comp);
}
@Override
public void set(T value) {
T old = get();
if (!Objects.equals(old, value)) {
cloneAndSet(label, value);
}
}
private void cloneAndSet(String group, Object value) {
Component comp = worldConfig.getProperties().get(group);
Component clone = compLib.copy(comp);
fieldMetadata.setValue(clone, value);
// notify the world generator about the new component
worldConfig.setProperty(label, clone);
}
}
private static class WorldConfigNumberBinding implements Binding<Float> {
private WorldConfigBinding<? extends Number> binding;
@SuppressWarnings("unchecked")
protected WorldConfigNumberBinding(WorldConfigurator config, String label, ComponentLibrary compLib, FieldMetadata<Object, ?> field) {
Class<?> type = field.getType();
if (type == Integer.TYPE || type == Integer.class) {
this.binding = new WorldConfigBinding<>(config, label, compLib,
(FieldMetadata<Object, Integer>) field);
} else if (type == Float.TYPE || type == Float.class) {
this.binding = new WorldConfigBinding<>(config, label, compLib,
(FieldMetadata<Object, Float>) field);
}
}
@Override
public Float get() {
Number val = binding.get();
if (val instanceof Float) {
// use boxed instance directly
return (Float) val;
}
// create a boxed instance otherwise
return val.floatValue();
}
@Override
@SuppressWarnings("unchecked")
public void set(Float value) {
Class<? extends Number> type = binding.fieldMetadata.getType();
if (type == Integer.TYPE || type == Integer.class) {
((Binding<Integer>) binding).set(value.intValue());
} else if (type == Float.TYPE || type == Float.class) {
((Binding<Float>) binding).set(value);
}
}
}
}

View file

@ -7,7 +7,6 @@
import org.terasology.engine.context.Context;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.core.TerasologyConstants;
import org.terasology.engine.core.modes.StateLoading;
import org.terasology.engine.core.module.ModuleManager;
import org.terasology.engine.game.GameManifest;
@ -19,15 +18,12 @@
import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems;
import org.terasology.engine.rendering.world.WorldSetupWrapper;
import org.terasology.engine.world.internal.WorldInfo;
import org.terasology.engine.world.time.WorldTime;
import org.terasology.gestalt.assets.ResourceUrn;
import org.terasology.nui.Canvas;
import org.terasology.nui.WidgetUtil;
import org.terasology.nui.widgets.UIImage;
import org.terasology.nui.widgets.UILabel;
import java.util.List;
public class StartPlayingScreen extends CoreScreenLayer {
public static final ResourceUrn ASSET_URI = new ResourceUrn("engine:startPlayingScreen");
@ -45,7 +41,6 @@ public class StartPlayingScreen extends CoreScreenLayer {
private TranslationSystem translationSystem;
private Texture texture;
private List<WorldSetupWrapper> worldSetupWrappers;
private UniverseWrapper universeWrapper;
private WorldSetupWrapper targetWorld;
@ -70,19 +65,8 @@ public void initialise() {
SimpleUri uri;
WorldInfo worldInfo;
//TODO: if we don't do that here, where do we do it? or does the world not show up in the game manifest?
//gameManifest.addWorld(worldInfo);
int i = 0;
for (WorldSetupWrapper world : worldSetupWrappers) {
if (world != targetWorld) {
i++;
uri = world.getWorldGeneratorInfo().getUri();
worldInfo = new WorldInfo(TerasologyConstants.MAIN_WORLD + i, world.getWorldName().toString(),
world.getWorldGenerator().getWorldSeed(), (long) (WorldTime.DAY_LENGTH * WorldTime.NOON_OFFSET), uri);
gameManifest.addWorld(worldInfo);
config.getUniverseConfig().addWorldManager(worldInfo);
}
}
gameEngine.changeState(new StateLoading(gameManifest, (universeWrapper.getLoadingAsServer())
? NetworkMode.DEDICATED_SERVER
@ -99,20 +83,18 @@ public void onOpened() {
UIImage previewImage = find("preview", UIImage.class);
previewImage.setImage(texture);
UILabel subitle = find("subtitle", UILabel.class);
subitle.setText(translationSystem.translate("${engine:menu#start-playing}") + " in " + targetWorld.getWorldName().toString());
UILabel subtitle = find("subtitle", UILabel.class);
subtitle.setText(translationSystem.translate("${engine:menu#start-playing}") + " in " + targetWorld.getWorldName().toString());
}
/**
* This method is called before the screen comes to the forefront to set the world in which the player is about to spawn.
*
* @param worldSetupWrapperList The world in which the player is going to spawn.
* @param targetWorldTexture The world texture generated in {@link WorldPreGenerationScreen} to be displayed on this screen.
*/
public void setTargetWorld(List<WorldSetupWrapper> worldSetupWrapperList, WorldSetupWrapper spawnWorld,
public void setTargetWorld(WorldSetupWrapper spawnWorld,
Texture targetWorldTexture, Context context) {
texture = targetWorldTexture;
worldSetupWrappers = worldSetupWrapperList;
universeWrapper = context.get(UniverseWrapper.class);
targetWorld = spawnWorld;
}

View file

@ -79,22 +79,18 @@ public class UniverseSetupScreen extends CoreScreenLayer {
@In
private Config config;
private List<WorldSetupWrapper> worlds = Lists.newArrayList();
private ModuleEnvironment environment;
private ModuleAwareAssetTypeManager assetTypeManager;
private Context context;
private int worldNumber;
private String selectedWorld = "";
private int indexOfSelectedWorld;
private WorldSetupWrapper copyOfSelectedWorld;
private WorldSetupWrapper selectedWorld;
@Override
public void initialise() {
setAnimationSystem(MenuAnimationSystems.createDefaultSwipeAnimation());
final UIDropdownScrollable<WorldGeneratorInfo> worldGenerator = find("worldGenerators", UIDropdownScrollable.class);
if (worldGenerator != null) {
worldGenerator.bindOptions(new ReadOnlyBinding<List<WorldGeneratorInfo>>() {
final UIDropdownScrollable<WorldGeneratorInfo> worldGenerators = find("worldGenerators", UIDropdownScrollable.class);
if (worldGenerators != null) {
worldGenerators.bindOptions(new ReadOnlyBinding<List<WorldGeneratorInfo>>() {
@Override
public List<WorldGeneratorInfo> get() {
// grab all the module names and their dependencies
@ -102,7 +98,10 @@ public List<WorldGeneratorInfo> get() {
final Set<Name> enabledModuleNames = new HashSet<>(getAllEnabledModuleNames());
final List<WorldGeneratorInfo> result = Lists.newArrayList();
for (WorldGeneratorInfo option : worldGeneratorManager.getWorldGenerators()) {
if (enabledModuleNames.contains(option.getUri().getModuleName())) {
//TODO: There should not be a reference from the engine to some module.
// The engine must be agnostic to what modules may do.
if (enabledModuleNames.contains(option.getUri().getModuleName())
&& !option.getUri().toString().equals("CoreWorlds:heightMap")) {
result.add(option);
}
}
@ -110,8 +109,8 @@ public List<WorldGeneratorInfo> get() {
return result;
}
});
worldGenerator.setVisibleOptions(3);
worldGenerator.bindSelection(new Binding<WorldGeneratorInfo>() {
worldGenerators.setVisibleOptions(3);
worldGenerators.bindSelection(new Binding<WorldGeneratorInfo>() {
@Override
public WorldGeneratorInfo get() {
// get the default generator from the config. This is likely to have a user triggered selection.
@ -138,7 +137,7 @@ public void set(WorldGeneratorInfo value) {
}
}
});
worldGenerator.setOptionRenderer(new StringTextRenderer<WorldGeneratorInfo>() {
worldGenerators.setOptionRenderer(new StringTextRenderer<WorldGeneratorInfo>() {
@Override
public String getString(WorldGeneratorInfo value) {
if (value != null) {
@ -148,56 +147,16 @@ public String getString(WorldGeneratorInfo value) {
}
});
}
final UIDropdownScrollable worldsDropdown = find("worlds", UIDropdownScrollable.class);
worldsDropdown.bindSelection(new Binding<String>() {
@Override
public String get() {
return selectedWorld;
}
@Override
public void set(String value) {
selectedWorld = value;
indexOfSelectedWorld = findIndex(worlds, selectedWorld);
}
});
WidgetUtil.trySubscribe(this, "close", button ->
triggerBackAnimation()
);
WidgetUtil.trySubscribe(this, "worldConfig", button -> {
final WorldSetupScreen worldSetupScreen = getManager().createScreen(WorldSetupScreen.ASSET_URI, WorldSetupScreen.class);
try {
if (!worlds.isEmpty() || !selectedWorld.isEmpty()) {
worldSetupScreen.setWorld(context, findWorldByName(), worldsDropdown);
triggerForwardAnimation(worldSetupScreen);
} else {
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class)
.setMessage("Worlds List Empty!", "No world found to configure.");
}
} catch (UnresolvedWorldGeneratorException e) {
logger.error("Can't configure the world! due to {}", e.getMessage());
}
});
WidgetUtil.trySubscribe(this, "addGenerator", button -> {
//TODO: there should not be a reference from the engine to some module - the engine must be agnostic to what
// modules may do
if (worldGenerator.getSelection().getUri().toString().equals("CoreWorlds:heightMap")) {
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class)
.setMessage("HeightMap not supported",
"HeightMap is not supported for advanced setup right now, a game template will be introduced soon.");
} else {
addNewWorld(worldGenerator.getSelection());
worldsDropdown.setOptions(worldNames());
}
});
WidgetUtil.trySubscribe(this, "continue", button -> {
final WorldPreGenerationScreen worldPreGenerationScreen =
getManager().createScreen(WorldPreGenerationScreen.ASSET_URI, WorldPreGenerationScreen.class);
if (!worlds.isEmpty()) {
addNewWorld(worldGenerators.getSelection());
if (selectedWorld != null) {
final WaitPopup<Boolean> loadPopup = getManager().pushScreen(WaitPopup.ASSET_URI, WaitPopup.class);
loadPopup.setMessage("Loading", "please wait ...");
loadPopup.onSuccess(result -> {
@ -231,14 +190,7 @@ public void set(String value) {
public void onOpened() {
super.onOpened();
worlds.clear();
worldNumber = 0;
final UIDropdownScrollable worldsDropdown = find("worlds", UIDropdownScrollable.class);
if (worldsDropdown != null) {
worldsDropdown.setOptions(worldNames());
}
selectedWorld = "";
indexOfSelectedWorld = findIndex(worlds, selectedWorld);
selectedWorld = null;
}
private Set<Name> getAllEnabledModuleNames() {
@ -262,23 +214,6 @@ private void recursivelyAddModuleDependencies(Set<Name> modules, Name moduleName
}
}
/**
* returns true if 'name' matches (case-insensitive) with another world already present
* @param name The world name to be checked
*/
public boolean worldNameMatchesAnother(String name) {
boolean taken = false;
for (WorldSetupWrapper worldTaken: worlds) {
if (worldTaken.getWorldName().toString().equalsIgnoreCase(name)) {
taken = true;
break;
}
}
return taken;
}
/**
* Called whenever the user decides to add a new world.
* @param worldGeneratorInfo The {@link WorldGeneratorInfo} object for the new world.
@ -286,24 +221,7 @@ public boolean worldNameMatchesAnother(String name) {
private void addNewWorld(WorldGeneratorInfo worldGeneratorInfo) {
String selectedWorldName = worldGeneratorInfo.getDisplayName();
while (worldNameMatchesAnother(selectedWorldName + "-" + worldNumber)) {
++worldNumber;
}
selectedWorld = worldGeneratorInfo.getDisplayName() + '-' + worldNumber;
worlds.add(new WorldSetupWrapper(new Name(worldGeneratorInfo.getDisplayName() + '-' + worldNumber), worldGeneratorInfo));
indexOfSelectedWorld = findIndex(worlds, selectedWorld);
++worldNumber;
}
/**
* This method refreshes the worlds drop-down menu when world name is changed and updates variable selectedWorld.
* @param worldsDropdown the drop-down to work on
*/
public void refreshWorldDropdown(UIDropdownScrollable worldsDropdown) {
worldsDropdown.setOptions(worldNames());
copyOfSelectedWorld = worlds.get(indexOfSelectedWorld);
selectedWorld = copyOfSelectedWorld.getWorldName().toString();
selectedWorld = new WorldSetupWrapper(new Name(selectedWorldName), worldGeneratorInfo);
}
/**
@ -342,23 +260,6 @@ public void setEnvironment(UniverseWrapper wrapper) {
}
}
/**
* Looks for the index of a selected world from the given list.
* @param worldsList the list to search
* @param worldName the name of the world to find
* @return the found index value or -1 if not found
*/
private int findIndex(List<WorldSetupWrapper> worldsList, String worldName) {
for (int i = 0; i < worldsList.size(); i++) {
WorldSetupWrapper currentWorldFromList = worldsList.get(i);
Name customName = currentWorldFromList.getWorldName();
if (customName.toString().equals(worldName)) {
return i;
}
}
return -1;
}
private void initAssets() {
ModuleEnvironment environment = context.get(ModuleManager.class).getEnvironment();
@ -380,40 +281,9 @@ private void initAssets() {
}
/**
* Create a list of the names of the world, so that they can be displayed as simple String
* in the drop-down.
* @return A list of world names encoded as a String
* @return the selected world in the drop-down.
*/
public List<String> worldNames() {
final List<String> worldNamesList = Lists.newArrayList();
for (WorldSetupWrapper world : worlds) {
worldNamesList.add(world.getWorldName().toString());
}
return worldNamesList;
}
/**
* This method takes the name of the selected world as String and return the corresponding
* WorldSetupWrapper object.
* @return {@link WorldSetupWrapper} object.
*/
public WorldSetupWrapper findWorldByName() {
for (WorldSetupWrapper world : worlds) {
if (world.getWorldName().toString().equals(selectedWorld)) {
return world;
}
}
return null;
}
public List<WorldSetupWrapper> getWorldsList() {
return worlds;
}
/**
* @return the selcted world in the drop-down.
*/
public String getSelectedWorld() {
public WorldSetupWrapper getSelectedWorld() {
return selectedWorld;
}

View file

@ -20,10 +20,10 @@
import org.terasology.gestalt.naming.Name;
import org.terasology.nui.WidgetUtil;
import org.terasology.nui.databinding.Binding;
import org.terasology.nui.widgets.UIDropdownScrollable;
import org.terasology.nui.widgets.UIImage;
import org.terasology.nui.widgets.UISlider;
import org.terasology.nui.widgets.UISliderOnChangeTriggeredListener;
import org.terasology.nui.widgets.UIText;
import org.terasology.engine.registry.In;
import org.terasology.engine.rendering.nui.CoreScreenLayer;
import org.terasology.engine.rendering.nui.NUIManager;
@ -63,9 +63,7 @@ public class WorldPreGenerationScreen extends CoreScreenLayer implements UISlide
private UIImage previewImage;
private Context context;
private PreviewGenerator previewGen;
private List<WorldSetupWrapper> worldList;
private String selectedWorld;
private List<String> worldNames;
private WorldSetupWrapper selectedWorld;
private int seedNumber;
private UISlider zoomSlider;
@ -81,15 +79,12 @@ public void setEnvironment(Context subContext) throws UnresolvedWorldGeneratorEx
context = subContext;
environment = context.get(ModuleEnvironment.class);
context.put(WorldGeneratorPluginLibrary.class, new TempWorldGeneratorPluginLibrary(environment, context));
worldList = context.get(UniverseSetupScreen.class).getWorldsList();
selectedWorld = context.get(UniverseSetupScreen.class).getSelectedWorld();
worldNames = context.get(UniverseSetupScreen.class).worldNames();
setWorldGenerators();
ensureWorldGeneratorIsSet();
selectedWorld.getWorldGenerator().setWorldSeed(createSeed(selectedWorld.getWorldName().toString()));
worldGenerator = findWorldByName(selectedWorld).getWorldGenerator();
final UIDropdownScrollable worldsDropdown = find("worlds", UIDropdownScrollable.class);
worldsDropdown.setOptions(worldNames);
worldGenerator = selectedWorld.getWorldGenerator();
genTexture();
List<Zone> previewZones = Lists.newArrayList(worldGenerator.getZones())
@ -111,52 +106,36 @@ public void initialise() {
zoomSlider.setUiSliderOnChangeTriggeredListener(this);
}
final UIDropdownScrollable worldsDropdown = find("worlds", UIDropdownScrollable.class);
worldsDropdown.bindSelection(new Binding<String>() {
final UIText worldName = find("worldName", UIText.class);
worldName.bindText(new Binding<String>() {
@Override
public String get() {
return selectedWorld;
return selectedWorld.getWorldName().toString();
}
@Override
public void set(String value) {
selectedWorld = value;
try {
if (findWorldByName(selectedWorld).getWorldGenerator() == null) {
worldGenerator = WorldGeneratorManager.createWorldGenerator(findWorldByName(selectedWorld)
.getWorldGeneratorInfo().getUri(), context, environment);
findWorldByName(selectedWorld).setWorldGenerator(worldGenerator);
} else {
worldGenerator = findWorldByName(selectedWorld).getWorldGenerator();
}
if (worldGenerator.getWorldSeed() == null) {
worldGenerator.setWorldSeed(createSeed(selectedWorld));
}
previewGen = new FacetLayerPreview(environment, worldGenerator);
updatePreview();
} catch (UnresolvedWorldGeneratorException e) {
e.printStackTrace();
}
// no-op
// field should be read-only
}
});
WidgetUtil.trySubscribe(this, "reRoll", button -> {
worldGenerator.setWorldSeed(createSeed(selectedWorld));
worldGenerator.setWorldSeed(createSeed(selectedWorld.getWorldName().toString()));
updatePreview();
});
StartPlayingScreen startPlayingScreen = getManager().createScreen(StartPlayingScreen.ASSET_URI, StartPlayingScreen.class);
WidgetUtil.trySubscribe(this, "continue", button -> {
startPlayingScreen.setTargetWorld(worldList, findWorldByName(selectedWorld), texture, context);
startPlayingScreen.setTargetWorld(selectedWorld, texture, context);
triggerForwardAnimation(startPlayingScreen);
});
WorldSetupScreen worldSetupScreen = getManager().createScreen(WorldSetupScreen.ASSET_URI, WorldSetupScreen.class);
WidgetUtil.trySubscribe(this, "config", button -> {
try {
if (!selectedWorld.isEmpty()) {
worldSetupScreen.setWorld(context, findWorldByName(selectedWorld), worldsDropdown);
if (!selectedWorld.getWorldName().isEmpty()) {
worldSetupScreen.setWorld(context, selectedWorld);
triggerForwardAnimation(worldSetupScreen);
} else {
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class)
@ -168,10 +147,6 @@ public void set(String value) {
});
WidgetUtil.trySubscribe(this, "close", button -> {
final UniverseSetupScreen universeSetupScreen =
getManager().createScreen(UniverseSetupScreen.ASSET_URI, UniverseSetupScreen.class);
UIDropdownScrollable worldsDropdownOfUniverse = universeSetupScreen.find("worlds", UIDropdownScrollable.class);
universeSetupScreen.refreshWorldDropdown(worldsDropdownOfUniverse);
triggerBackAnimation();
});
@ -185,15 +160,15 @@ public void onOpened() {
super.onOpened();
try {
if (findWorldByName(selectedWorld).getWorldGenerator() == null) {
worldGenerator = WorldGeneratorManager.createWorldGenerator(findWorldByName(selectedWorld)
if (selectedWorld.getWorldGenerator() == null) {
worldGenerator = WorldGeneratorManager.createWorldGenerator(selectedWorld
.getWorldGeneratorInfo().getUri(), context, environment);
findWorldByName(selectedWorld).setWorldGenerator(worldGenerator);
selectedWorld.setWorldGenerator(worldGenerator);
} else {
worldGenerator = findWorldByName(selectedWorld).getWorldGenerator();
worldGenerator = selectedWorld.getWorldGenerator();
}
if (worldGenerator.getWorldSeed().isEmpty()) {
worldGenerator.setWorldSeed(createSeed(selectedWorld));
worldGenerator.setWorldSeed(createSeed(selectedWorld.getWorldName().toString()));
}
previewGen = new FacetLayerPreview(environment, worldGenerator);
updatePreview();
@ -202,12 +177,13 @@ public void onOpened() {
}
}
//TODO: this does not actually only set it when configure is called from WorldPreGenerationScreen, but also if called from UniverseSetupScreen
/**
* Set seletedWorld when configure from WorldPreGenerationScreen
* @param newNameToSet
*/
public void setName(Name newNameToSet) {
selectedWorld = newNameToSet.toString();
selectedWorld.setWorldName(newNameToSet);
}
/**
@ -252,21 +228,6 @@ private void updatePreview() {
popup.startOperation(operation, true);
}
/**
* This method takes the name of the world selected in the worldsDropdown
* as String and return the corresponding WorldSetupWrapper object.
*
* @return {@link WorldSetupWrapper} object of the selected world.
*/
private WorldSetupWrapper findWorldByName(String searchWorld) {
for (WorldSetupWrapper world : worldList) {
if (world.getWorldName().toString().equals(searchWorld)) {
return world;
}
}
return null;
}
/**
* Creates a unique world seed by appending the world name with an incrementing number, on top of the universe seed.
*
@ -278,17 +239,15 @@ private String createSeed(String world) {
return seed + world + seedNumber++;
}
private void setWorldGenerators() {
for (WorldSetupWrapper worldSetupWrapper : worldList) {
if (worldSetupWrapper.getWorldGenerator() == null) {
try {
worldSetupWrapper.setWorldGenerator(WorldGeneratorManager.createWorldGenerator(findWorldByName(
worldSetupWrapper.getWorldName().toString()).getWorldGeneratorInfo().getUri(), context, environment));
} catch (UnresolvedWorldGeneratorException e) {
e.printStackTrace();
}
private void ensureWorldGeneratorIsSet() {
if (selectedWorld.getWorldGenerator() == null) {
try {
selectedWorld.setWorldGenerator(WorldGeneratorManager.createWorldGenerator(
selectedWorld.getWorldGeneratorInfo().getUri(), context, environment));
} catch (UnresolvedWorldGeneratorException e) {
//TODO: this will likely fail at game creation time later-on due to lack of world generator - don't just ignore this
e.printStackTrace();
}
worldSetupWrapper.getWorldGenerator().setWorldSeed(createSeed(worldSetupWrapper.getWorldName().toString()));
}
}

View file

@ -28,7 +28,6 @@
import org.terasology.nui.properties.Property;
import org.terasology.nui.properties.PropertyOrdering;
import org.terasology.nui.properties.PropertyProvider;
import org.terasology.nui.widgets.UIDropdownScrollable;
import org.terasology.nui.widgets.UILabel;
import org.terasology.nui.widgets.UIText;
import org.terasology.reflection.metadata.FieldMetadata;
@ -60,17 +59,12 @@ public class WorldSetupScreen extends CoreScreenLayer {
private Context context;
private WorldConfigurator oldWorldConfig;
private Name newWorldName;
private UIDropdownScrollable worldsDropdown;
@Override
public void initialise() {
setAnimationSystem(MenuAnimationSystems.createDefaultSwipeAnimation());
WidgetUtil.trySubscribe(this, "close", button -> {
final UniverseSetupScreen universeSetupScreen =
getManager().createScreen(UniverseSetupScreen.ASSET_URI, UniverseSetupScreen.class);
final WorldPreGenerationScreen worldPreGenerationScreen =
getManager().createScreen(WorldPreGenerationScreen.ASSET_URI, WorldPreGenerationScreen.class);
WidgetUtil.trySubscribe(this, "close", button -> {
UIText customWorldName = find("customisedWorldName", UIText.class);
boolean goBack = false;
@ -83,10 +77,6 @@ public void initialise() {
} else if (customWorldName.getText().equalsIgnoreCase(world.getWorldName().toString())) {
//same name as before: go back to universe setup
goBack = true;
} else if (universeSetupScreen.worldNameMatchesAnother(customWorldName.getText())) {
//if same name is already used, inform user with a popup
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class)
.setMessage("Name Already Used!", "Please use a different name for this world");
} else {
//no match found: go back to universe setup
goBack = true;
@ -95,8 +85,6 @@ public void initialise() {
if (goBack) {
newWorldName = new Name(customWorldName.getText());
world.setWorldName(newWorldName);
universeSetupScreen.refreshWorldDropdown(worldsDropdown);
worldPreGenerationScreen.setName(newWorldName);
triggerBackAnimation();
}
});
@ -129,11 +117,10 @@ public void onOpened() {
* @param worldSelected the world whose configurations are to be changed.
* @throws UnresolvedWorldGeneratorException
*/
public void setWorld(Context subContext, WorldSetupWrapper worldSelected, UIDropdownScrollable dropDown)
public void setWorld(Context subContext, WorldSetupWrapper worldSelected)
throws UnresolvedWorldGeneratorException {
world = worldSelected;
context = subContext;
worldsDropdown = dropDown;
SimpleUri worldGenUri = worldSelected.getWorldGeneratorInfo().getUri();
environment = context.get(ModuleEnvironment.class);
context.put(WorldGeneratorPluginLibrary.class, new TempWorldGeneratorPluginLibrary(environment, context));

View file

@ -254,7 +254,6 @@
"motion-blur": "motion-blur",
"mouse-sensitivity": "mouse-sensitivity",
"movement-dead-zone": "movement-dead-zone",
"multi-world-warning": "multi-world-warning",
"multiplayer-identities": "multiplayer-identities",
"music-volume": "music-volume",
"new-binding": "new-binding",
@ -440,6 +439,7 @@
"world-config-preview": "world-config-preview",
"world-configuration": "world-configuration",
"world-generator": "world-generator",
"world-name": "world-name",
"world-pre-generation": "world-pre-generation",
"world-seed": "world-seed",
"world-setup": "world-setup",

View file

@ -250,7 +250,6 @@
"motion-blur": "Rozmazání pohybem",
"mouse-sensitivity": "Citlivost myši",
"movement-dead-zone": "Mrtvá zóna pohybové osy",
"multi-world-warning": "VAROVÁNÍ: Multi-world není dokončen, pouze finálně vybraný svět bude k dispozici!",
"music-volume": "Hlasitost hudby",
"new-binding": "Nové přiřazení",
"new-game-title": "Nová hra",
@ -290,7 +289,7 @@
"player-settings-identities-import": "Importovat...",
"player-settings-title": "Nastavení hráče",
"please-wait": "Prosím počkej...",
"pregeneration-description": "Vyber svět ze seznamu k náhledu. Klikni na Přegenerovat k náhodnému výběru nového seedu nebo na Nastavit pro detailní úpravu světa",
"pregeneration-description": "Klikni na Přegenerovat k náhodnému výběru nového seedu nebo na Nastavit pro detailní úpravu světa",
"preview-world-title": "Náhled světa...",
"preview-zoom-factor": "Přiblížení",
"previous-toolbar-item": "Předchozí předmět v toolbaru",
@ -396,7 +395,7 @@
"this-language-English": "Czech",
"this-language-native": "Česky",
"universe-setup": "Nastavení Vesmíru",
"universe-setup-description": "Vyber generátor světa a přidej ho. Poté vyber a nastav světy z čerstvě vyplněného seznamu světů.",
"universe-setup-description": "Vyber generátor světa.",
"update-module": "Aktualizovat",
"time-progression-during-pre-generation": "Postup času během předgenerace",
"validation-username-max-length": "Jméno nesmí být delší než 100 znaků",

View file

@ -248,5 +248,6 @@
"widget-selection-prompt": "Bitte wähle ein Widget aus:",
"widget-selection-title": "Widget-Auswahl",
"world-config-preview": "Details ...",
"world-name": "Weltname",
"world-seed": "Seed"
}

View file

@ -259,7 +259,6 @@
"motion-blur": "Motion Blur",
"mouse-sensitivity": "Mouse Sensitivity",
"movement-dead-zone": "Movement Axis Dead Zone",
"multi-world-warning": "WARNING: Multi-world is not completed, only the final selected world will be available!",
"multiplayer-identities": "Multiplayer identities:",
"music-volume": "Music Volume",
"new-binding": "New binding",
@ -299,7 +298,7 @@
"player-settings-identities-import": "Import...",
"player-settings-title": "Player Settings",
"please-wait": "Please wait...",
"pregeneration-description": "Select a world in the drop-down to see it previewed. Click Re-Roll to randomly pick a new seed or Configure to manually tweak the world further",
"pregeneration-description": "Click Re-Roll to randomly pick a new seed or Configure to manually tweak the world further",
"preview-world-title": "Preview World...",
"preview-zoom-factor": "Zoom",
"previous-toolbar-item": "Previous Toolbar Item",
@ -411,7 +410,7 @@
"this-language-English": "English",
"this-language-native": "English",
"universe-setup": "Universe Setup",
"universe-setup-description": "Pick a world generator and add it. Then select and configure worlds from the freshly filled Worlds dropdown.",
"universe-setup-description": "Pick a world generator.",
"update-module": "Update",
"time-progression-during-pre-generation": "Time progression during pre-generation",
"validation-username-max-length": "Username can't be longer then 100 chars",
@ -457,6 +456,7 @@
"world-config-preview": "Details...",
"world-configuration": "World Configuration",
"world-generator": "World Generator",
"world-name": "World Name",
"world-pre-generation": "World Pre-generation",
"world-seed": "Seed",
"world-setup": "World Setup",

View file

@ -249,7 +249,6 @@
"motion-blur": "Flou cinétique",
"mouse-sensitivity": "Sensibilité de la souris",
"movement-dead-zone": "Mouvement dans l'axe de la zone morte",
"multi-world-warning": "AVERTISSEMENT: le multi-monde n'est pas terminé, seul le dernier monde sélectionné sera disponible !",
"music-volume": "Volume de la musique",
"new-binding": "Nouveau racourcie",
"new-game-title": "Nouvelle partie",
@ -288,7 +287,7 @@
"player-settings-identities-import": "Import...",
"player-settings-title": "Paramètres du joueur",
"please-wait": "Veuillez patienter...",
"pregeneration-description": "Sélectionnez un monde dans le menu déroulant pour le voir en aperçu. Cliquez sur Jet-de-dés pour choisir au hasard une nouvelle graine ou sur Configurer pour modifier manuellement le monde",
"pregeneration-description": "Cliquez sur Jet-de-dés pour choisir au hasard une nouvelle graine ou sur Configurer pour modifier manuellement le monde",
"preview-world-title": "Prévisualisation du monde...",
"preview-zoom-factor": "Zoom",
"previous-toolbar-item": "Barre d'outil précédente",
@ -392,7 +391,7 @@
"this-language-English": "French",
"this-language-native": "Français",
"universe-setup": "Configuration de l'univers",
"universe-setup-description": "Choisissez un générateur de monde et ajoutez-le. Ensuite, sélectionnez et configurez des mondes dans la liste déroulante des mondes fraîchement remplie.",
"universe-setup-description": "Choisissez un générateur de monde.",
"update-module": "Mise à jour",
"time-progression-during-pre-generation": "Progression de temps pendant la pré-génération",
"validation-username-max-length": "Le nom d'utilisateur ne peut pas être plus long que 100 caractères",

View file

@ -252,7 +252,6 @@
"motion-blur": "モーション・ブラー",
"mouse-sensitivity": "マウスの感度",
"movement-dead-zone": "移動する座標は行けません",
"multi-world-warning": "警告: 多重ワールドは終わってません,最後に選択されたワールドのみ利用可能です!",
"music-volume": "音量",
"new-binding": "新しいバインディング",
"new-game-title": "ニュー・ゲーム",
@ -291,7 +290,7 @@
"player-settings-identities-import": "インポート...",
"player-settings-title": "プレイヤーのセッティング",
"please-wait": "しばらくお待ちください",
"pregeneration-description": "ドロップ・ダウンから観たいワールドを選べ。リロールをクリックすることで新しいシードをランダムに選びます。または手動でワールドを微設定することもできます",
"pregeneration-description": "「再ロール」をクリックして、新しいシードをランダムに選択します。 または、世界を手動で調整することもできます",
"preview-world-title": "ワールドのプレビュー...",
"preview-zoom-factor": "ズーム",
"previous-toolbar-item": "前のツールバーの項目",
@ -396,7 +395,7 @@
"this-language-English": "Japanese",
"this-language-native": "日本語",
"universe-setup": "全体の設定",
"universe-setup-description": 新しいワールドをドロップダウンから選んで設定したとき、ワールドジェネレータを選択し追加してください。",
"universe-setup-description": "ワールドジェネレーターを選択します。",
"update-module": "アップデート",
"time-progression-during-pre-generation": "事前生成中の時間経過",
"validation-username-max-length": "ユーザーネームは100文字以上長くできません",

View file

@ -249,7 +249,6 @@
"motion-blur": "Lëvizja e turbullimit",
"mouse-sensitivity": "Ndjeshmëri e mausit",
"movement-dead-zone": "Movement Axis Dead Zone",
"multi-world-warning": "WARNING: Multi-world is not completed, only the final selected world will be available!",
"music-volume": "Niveli i Muzikës",
"new-binding": "Lidhja e re",
"new-game-title": "Lojë e Re",
@ -288,7 +287,7 @@
"player-settings-identities-import": "Import...",
"player-settings-title": "Rrëgullimet e Lojtarit",
"please-wait": "Te Lutem Prit...",
"pregeneration-description": "Select a world in the drop-down to see it previewed. Click Re-Roll to randomly pick a new seed or Configure to manually tweak the world further",
"pregeneration-description": "Click Re-Roll to randomly pick a new seed or Configure to manually tweak the world further",
"preview-world-title": "Për të Parë Botën...",
"preview-zoom-factor": "Zoom",
"previous-toolbar-item": "Objekti i mëparshëm i toolbarit",
@ -392,7 +391,7 @@
"this-language-English": "Albanian",
"this-language-native": "Shqip",
"universe-setup": "Rrëgullimi i universit ",
"universe-setup-description": "Zgjidh një gjenerator të botës dhe shtoje atë. Pastaj zgjidhni dhe konfiguroni botët nga rishtarja e mbushur Botët dropdown.",
"universe-setup-description": "Zgjidhni një gjenerator botëror.",
"update-module": "Update",
"time-progression-during-pre-generation": "Progresi i kohës gjatë pre-lindja",
"validation-username-max-length": "Emri nuk mun të jetë më i gjatë se 100 shenja",

View file

@ -259,7 +259,6 @@
"motion-blur": "Розмиття",
"mouse-sensitivity": "Чутливість мишки",
"movement-dead-zone": "Мертва зона осі руху",
"multi-world-warning": "ПОПЕРЕДЖЕННЯ: мульти-світи незакінчені, буде доступний тільки останній обраний світ!",
"multiplayer-identities": "Облікові записи:",
"music-volume": "Гучність музики",
"new-binding": "Нова прив'язка",
@ -299,7 +298,7 @@
"player-settings-identities-import": "Імпортувати...",
"player-settings-title": "Налаштування гравця",
"please-wait": "Будь ласка, зачекайте...",
"pregeneration-description": "Виберіть світ зі списку для попереднього перегляду. Натисніть 'Перегенерувати', щоб довільно обрати нове зерно, або 'Конфігурація', щоб власноруч редагувати світ.",
"pregeneration-description": "Натисніть 'Перегенерувати', щоб довільно обрати нове зерно, або 'Конфігурація', щоб власноруч редагувати світ.",
"preview-world-title": "Попередній перегляд...",
"preview-zoom-factor": "Масштаб",
"previous-toolbar-item": "Попередній інструмент",
@ -411,7 +410,7 @@
"this-language-English": "Ukrainian",
"this-language-native": "Українська",
"universe-setup": "Налаштування світу",
"universe-setup-description": "Виберіть генератор світу і додайте його - потім виберіть та налаштуйте світи з новоствореного списку.",
"universe-setup-description": "Виберіть генератор світу.",
"update-module": "Оновити",
"time-progression-during-pre-generation": "Прогрес часу під час попереднього генерування",
"validation-username-max-length": "Логін має бути коротшим за 100 символів",

View file

@ -1,186 +0,0 @@
{
"type": "engine:previewWorldScreen",
"skin": "engine:mainMenu",
"contents": {
"type": "relativeLayout",
"contents": [
{
"type": "UILabel",
"id": "title",
"family": "title",
"text": "${engine:menu#preview-world-title}",
"layoutInfo": {
"height": 48,
"position-horizontal-center": {},
"position-top": {
"target": "TOP",
"offset": 48
}
}
},
{
"type": "UIBox",
"id": "container",
"layoutInfo": {
"width": 720,
"position-horizontal-center": {},
"position-top": {
"target": "BOTTOM",
"offset": 16,
"widget": "title"
},
"position-bottom": {
"target": "TOP",
"widget": "close",
"offset": 16
}
}
},
{
"type": "ColumnLayout",
"columns": 2,
"verticalSpacing": 16,
"horizontalSpacing": 8,
"column-widths": [0.53, 0.47],
"layoutInfo": {
"width": 704,
"position-horizontal-center": {},
"position-top": {
"target": "TOP",
"widget": "container",
"offset": 8
},
"position-bottom": {
"target": "TOP",
"widget": "close",
"offset": 24
}
},
"contents": [
{
"type": "UIImage",
"skin": "framed_image",
"id": "preview"
},
{
"type": "RelativeLayout",
"family": "description",
"contents": [
{
"type": "UILabel",
"text": "${engine:menu#preview-zoom-factor}:",
"id": "zoomLabel",
"layoutInfo": {
"use-content-height": true,
"position-horizontal-center": {},
"position-top": {
"target": "TOP"
}
}
},
{
"type": "UISlider",
"id": "zoomSlider",
"minimum": 1.0,
"range": 7.0,
"increment": 1.0,
"precision": 0,
"layoutInfo": {
"use-content-height": true,
"position-horizontal-center": {},
"position-top": {
"target": "BOTTOM",
"widget": "zoomLabel"
}
}
},
{
"type": "UIDropdown",
"id": "zoneSelector",
"layoutInfo": {
"use-content-height": true,
"position-top": {
"target": "BOTTOM",
"widget": "zoomSlider",
"offset": 8
}
}
},
{
"type": "UILabel",
"id": "seedLabel",
"text": "${engine:menu#world-seed}:",
"layoutInfo": {
"use-content-height": true,
"position-top": {
"target": "BOTTOM",
"widget": "zoneSelector",
"offset": 8
}
}
},
{
"type": "UIText",
"id": "seed",
"layoutInfo": {
"use-content-height": true,
"position-top": {
"target": "BOTTOM",
"widget": "seedLabel"
}
}
},
{
"type": "ScrollableArea",
"content": {
"type": "propertyLayout",
"id": "properties",
"rowConstraints": "[min]"
},
"layoutInfo": {
"position-horizontal-center": {},
"position-top": {
"target": "BOTTOM",
"offset": 8,
"widget": "seed"
},
"position-bottom": {
"target": "TOP",
"offset": 8,
"widget": "apply"
}
}
},
{
"type": "UIButton",
"text": "${engine:menu#create-preview}",
"id": "apply",
"layoutInfo": {
"height": 32,
"position-bottom": {
"target": "BOTTOM",
"offset": 4
}
}
}
]
}
]
},
{
"type": "UIButton",
"text": "${engine:menu#back}",
"id": "close",
"layoutInfo": {
"width": 128,
"height": 32,
"position-horizontal-center": {},
"position-bottom": {
"target": "BOTTOM",
"offset": 48
}
}
}
]
}
}

View file

@ -18,24 +18,9 @@
}
}
},
{
"type": "UILabel",
"id": "warning",
"text": "${engine:menu#multi-world-warning}",
"family": "warning",
"layoutInfo": {
"height": 12,
"position-horizontal-center": {},
"position-bottom": {
"target": "TOP",
"widget": "mainBox",
"offset": 36
}
}
},
{
"type": "UIBox",
"id":"mainBox",
"id": "mainBox",
"content": {
"type": "ColumnLayout",
"columns": 1,
@ -59,30 +44,8 @@
"type": "UIDropdownScrollable",
"id": "worldGenerators",
"layoutInfo": {
"relativeWidth": 0.50
"relativeWidth": 1.00
}
},
{
"type": "UISpace",
"size": [
1,
8
]
},
{
"type": "UIButton",
"id": "addGenerator",
"text": "${engine:menu#add}",
"layoutInfo": {
"relativeWidth": 0.30
}
},
{
"type": "UISpace",
"size": [
1,
8
]
}
]
},
@ -92,46 +55,6 @@
1,
8
]
},
{
"type": "UILabel",
"text": "${engine:menu#game-worlds}:",
"family": "left-label"
},
{
"type": "RowLayout",
"horizontalSpacing": 4,
"contents": [
{
"type": "UIDropdownScrollable",
"id": "worlds",
"layoutInfo": {
"relativeWidth": 0.50
}
},
{
"type": "UISpace",
"size": [
1,
8
]
},
{
"type": "UIButton",
"id": "worldConfig",
"text": "${engine:menu#config}",
"layoutInfo": {
"relativeWidth": 0.30
}
},
{
"type": "UISpace",
"size": [
1,
8
]
}
]
}
]
},

View file

@ -41,7 +41,10 @@
"id": "coreLayout",
"columns": 2,
"horizontalSpacing": 8,
"column-widths": [0.53, 0.47],
"column-widths": [
0.53,
0.47
],
"layoutInfo": {
"width": 704,
"use-content-height": true,
@ -133,16 +136,11 @@
}
},
{
"type": "UIDropdownScrollable",
"id": "worlds",
"layoutInfo": {
"position-horizontal-center": {},
"position-top": {
"target": "BOTTOM",
"widget": "worldGenLabel",
"offset": 0
}
}
"type": "engine:UIText",
"id": "worldName",
"readOnly": true,
"enabled": false,
"layoutInfo": {}
},
{
"type": "RowLayout",

View file

@ -7,7 +7,7 @@
{
"type": "UIBox",
"layoutInfo": {
"width": 200,
"width": 400,
"use-content-height": true,
"position-horizontal-center": {},
"position-vertical-center": {}

View file

@ -62,7 +62,7 @@
"contents": [
{
"type": "RowLayout",
"id": "shape",
"id": "customNameWorld",
"layoutInfo": {
"use-content-height": true,
"position-horizontal-center": {},
@ -73,32 +73,7 @@
"contents": [
{
"type": "UILabel",
"text": "World Shape:",
"enabled": false
},
{
"type": "UIDropdownScrollable",
"id": "worlds",
"enabled": false
}
]
},
{
"type": "RowLayout",
"id": "customNameWorld",
"layoutInfo": {
"use-content-height": true,
"position-horizontal-center": {},
"position-top": {
"target": "BOTTOM",
"offset": 8,
"widget": "shape"
}
},
"contents": [
{
"type": "UILabel",
"text": "World Name"
"text": "${engine:menu#world-name}"
},
{
"type": "UIText",

View file

@ -1,11 +0,0 @@
# Boo eclipse! - every eclipse project needs its own .gitignore file
# Ignore Eclipse
/.checkstyle
/.project
/.classpath
/.settings/
/bin/
# Ignore gradle
/build/

View file

@ -1,94 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
// The Editor facade is responsible for the (shader) editor - a plain Java application runnable on PCs
plugins {
id "application"
}
// Grab all the common stuff like plugins to use, artifact repositories, code analysis config
apply from: "$rootDir/config/gradle/publish.gradle"
// Base the engine tests on the same version number as the engine
version = project(':engine').version
println "TeraEd VERSION: $version"
// Jenkins-Artifactory integration catches on to this as part of the Maven-type descriptor
group = 'org.terasology.facades'
sourceSets {
// Adjust output path (changed with the Gradle 6 upgrade, this puts it back)
main.java.outputDir = new File("$buildDir/classes")
test.java.outputDir = new File("$buildDir/testClasses")
}
dependencies {
implementation project(':engine')
implementation "org.terasology:reflections:0.9.12-MB"
runtimeOnly(platform(project(":modules")))
// For the "natives" configuration make it depend on the native files from LWJGL
implementation platform("org.lwjgl:lwjgl-bom:$LwjglVersion")
["natives-linux", "natives-windows", "natives-macos"].forEach {
implementation "org.lwjgl:lwjgl::$it"
implementation "org.lwjgl:lwjgl-assimp::$it"
implementation "org.lwjgl:lwjgl-glfw::$it"
implementation "org.lwjgl:lwjgl-openal::$it"
implementation "org.lwjgl:lwjgl-opengl::$it"
implementation "org.lwjgl:lwjgl-stb::$it"
}
implementation "org.lwjgl:lwjgl-jawt"
implementation(group: 'com.google.guava', name: 'guava', version: '30.1-jre')
implementation(project(":subsystems:DiscordRPC"))
implementation(project(":subsystems:TypeHandlerLibrary"))
implementation(group: 'org.lwjglx', name: 'lwjgl3-awt', version: '0.1.7') {
exclude group: 'org.lwjgl', module: ''
}
}
application {
mainClass = "org.terasology.editor.TeraEd"
}
run {
description = "Run 'TeraEd' to configure graphics shader parameters in a standard PC application"
group = "terasology run"
workingDir = rootDir
args "-homedir"
}
task editor(type: JavaExec) {
description = "Run 'TeraEd' to configure graphics shader parameters in a standard PC application"
group = "terasology run"
// Dependencies: natives + all modules & the PC facade itself (which will trigger the engine)
dependsOn rootProject.extractNatives
dependsOn classes
// Run arguments
main = 'org.terasology.editor.TeraEd'
workingDir = rootDir
String[] runArgs = ["-homedir"]
args runArgs
// Classpath: PC itself, engine classes, engine dependencies. Not modules or natives since the engine finds those
classpath sourceSets.main.output.classesDirs
classpath sourceSets.main.output.resourcesDir
classpath project(':engine').sourceSets.main.output.classesDirs
classpath project(':engine').configurations.runtimeClasspath
}
// Prep an IntelliJ module for the facade
idea {
module {
// Change around the output a bit
inheritOutputDirs = false
outputDir = file('build/classes')
testOutputDir = file('build/testClasses')
}
}

View file

@ -1,118 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.editor.properties.SceneProperties;
import org.terasology.editor.subsystem.AwtInput;
import org.terasology.editor.subsystem.LwjglPortlet;
import org.terasology.editor.ui.MainWindow;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.PathManager;
import org.terasology.engine.core.TerasologyEngine;
import org.terasology.engine.core.TerasologyEngineBuilder;
import org.terasology.engine.core.modes.StateMainMenu;
import org.terasology.engine.core.subsystem.config.BindsSubsystem;
import org.terasology.engine.core.subsystem.lwjgl.LwjglAudio;
import org.terasology.engine.core.subsystem.lwjgl.LwjglTimer;
import org.terasology.engine.monitoring.PerformanceMonitor;
import javax.swing.JPopupMenu;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
* TeraEd main class.
*/
@SuppressWarnings("serial")
public final class TeraEd extends JWindow {
private MainWindow mainWindow;
private TerasologyEngine engine;
private final Logger logger = LoggerFactory.getLogger(TeraEd.class);
private SceneProperties sceneProperties;
public static void main(String[] args) {
new TeraEd().run();
}
public void run() {
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
try {
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
// If Nimbus is not available, you can set the GUI to another look and feel.
logger.warn("Failed to set look and feel to Nimbus", e);
}
try {
LwjglPortlet portlet = new LwjglPortlet();
PathManager.getInstance().useDefaultHomePath();
engine = new TerasologyEngineBuilder()
.add(new LwjglTimer())
.add(new LwjglAudio())
.add(new AwtInput())
.add(new BindsSubsystem())
.add(portlet).build();
if (!GLFW.glfwInit()) {
throw new RuntimeException("Failed to initialize GLFW");
}
sceneProperties = new SceneProperties(engine);
mainWindow = new MainWindow(this, engine);
portlet.createCanvas();
AWTGLCanvas canvas = portlet.getCanvas();
engine.subscribeToStateChange(mainWindow);
engine.initializeRun(new StateMainMenu());
mainWindow.getViewport().setTerasology(canvas);
portlet.initInputs();
Runnable renderLoop = new Runnable() {
public void run() {
if (canvas.isValid()) {
canvas.render();
}
SwingUtilities.invokeLater(this);
}
};
// Setup swing thread as game thread
PerformanceMonitor.startActivity("Other");
SwingUtilities.invokeAndWait(portlet::setupThreads);
SwingUtilities.invokeLater(renderLoop);
PerformanceMonitor.endActivity();
} catch (Throwable t) {
logger.error("Uncaught Exception", t);
}
}
public GameEngine getEngine() {
return engine;
}
public MainWindow getMainWindow() {
return mainWindow;
}
public SceneProperties getSceneProperties() {
return sceneProperties;
}
}

View file

@ -1,264 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.input;
import com.google.common.collect.Lists;
import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.input.ButtonState;
import org.terasology.input.Input;
import org.terasology.input.InputType;
import org.terasology.input.Keyboard;
import org.terasology.input.device.CharKeyboardAction;
import org.terasology.input.device.KeyboardDevice;
import org.terasology.input.device.RawKeyboardAction;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Queue;
/**
* AWT converting keyboard device representation.
*/
public class AwtKeyboardDevice implements KeyboardDevice {
private static final Logger logger = LoggerFactory.getLogger(AwtKeyboardDevice.class);
private static final TIntIntMap AWT_TO_TERA_MAPPING = new TIntIntHashMap();
private static final TIntObjectMap<TIntIntHashMap> AWT_TO_TERA_EXTRA = new TIntObjectHashMap<>();
static {
//TODO: test and cleanup keys
// AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NONE, Keyboard.KeyId.NONE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_ESCAPE, Keyboard.KeyId.ESCAPE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_1, Keyboard.KeyId.KEY_1);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_2, Keyboard.KeyId.KEY_2);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_3, Keyboard.KeyId.KEY_3);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_4, Keyboard.KeyId.KEY_4);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_5, Keyboard.KeyId.KEY_5);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_6, Keyboard.KeyId.KEY_6);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_7, Keyboard.KeyId.KEY_7);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_8, Keyboard.KeyId.KEY_8);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_9, Keyboard.KeyId.KEY_9);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_0, Keyboard.KeyId.KEY_0);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_MINUS, Keyboard.KeyId.MINUS);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_BACK_SPACE, Keyboard.KeyId.BACKSPACE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_TAB, Keyboard.KeyId.TAB);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_Q, Keyboard.KeyId.Q);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_W, Keyboard.KeyId.W);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_E, Keyboard.KeyId.E);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_R, Keyboard.KeyId.R);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_T, Keyboard.KeyId.T);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_Y, Keyboard.KeyId.Y);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_U, Keyboard.KeyId.U);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_I, Keyboard.KeyId.I);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_O, Keyboard.KeyId.O);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_P, Keyboard.KeyId.P);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_OPEN_BRACKET, Keyboard.KeyId.LEFT_BRACKET);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_CLOSE_BRACKET, Keyboard.KeyId.RIGHT_BRACKET);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_A, Keyboard.KeyId.A);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_S, Keyboard.KeyId.S);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_D, Keyboard.KeyId.D);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F, Keyboard.KeyId.F);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_G, Keyboard.KeyId.G);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_H, Keyboard.KeyId.H);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_J, Keyboard.KeyId.J);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_K, Keyboard.KeyId.K);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_L, Keyboard.KeyId.L);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_SEMICOLON, Keyboard.KeyId.SEMICOLON);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DEAD_ACUTE, Keyboard.KeyId.APOSTROPHE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DEAD_GRAVE, Keyboard.KeyId.GRAVE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_BACK_SLASH, Keyboard.KeyId.BACKSLASH);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_Z, Keyboard.KeyId.Z);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_X, Keyboard.KeyId.X);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_C, Keyboard.KeyId.C);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_V, Keyboard.KeyId.V);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_B, Keyboard.KeyId.B);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_N, Keyboard.KeyId.N);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_M, Keyboard.KeyId.M);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_COMMA, Keyboard.KeyId.COMMA);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_PERIOD, Keyboard.KeyId.PERIOD);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_SLASH, Keyboard.KeyId.SLASH);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_MULTIPLY, Keyboard.KeyId.NUMPAD_MULTIPLY);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_SPACE, Keyboard.KeyId.SPACE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_CAPS_LOCK, Keyboard.KeyId.CAPS_LOCK);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F1, Keyboard.KeyId.F1);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F2, Keyboard.KeyId.F2);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F3, Keyboard.KeyId.F3);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F4, Keyboard.KeyId.F4);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F5, Keyboard.KeyId.F5);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F6, Keyboard.KeyId.F6);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F7, Keyboard.KeyId.F7);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F8, Keyboard.KeyId.F8);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F9, Keyboard.KeyId.F9);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F10, Keyboard.KeyId.F10);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUM_LOCK, Keyboard.KeyId.NUM_LOCK);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_SCROLL_LOCK, Keyboard.KeyId.SCROLL_LOCK);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD7, Keyboard.KeyId.NUMPAD_7);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD8, Keyboard.KeyId.NUMPAD_8);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD9, Keyboard.KeyId.NUMPAD_9);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_SUBTRACT, Keyboard.KeyId.NUMPAD_MINUS);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD4, Keyboard.KeyId.NUMPAD_4);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD5, Keyboard.KeyId.NUMPAD_5);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD6, Keyboard.KeyId.NUMPAD_6);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_ADD, Keyboard.KeyId.NUMPAD_PLUS);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD1, Keyboard.KeyId.NUMPAD_1);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD2, Keyboard.KeyId.NUMPAD_2);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD3, Keyboard.KeyId.NUMPAD_3);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_NUMPAD0, Keyboard.KeyId.NUMPAD_0);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DECIMAL, Keyboard.KeyId.NUMPAD_PERIOD);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F11, Keyboard.KeyId.F11);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F12, Keyboard.KeyId.F12);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F13, Keyboard.KeyId.F13);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F14, Keyboard.KeyId.F14);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F15, Keyboard.KeyId.F15);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F16, Keyboard.KeyId.F16);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F17, Keyboard.KeyId.F17);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F18, Keyboard.KeyId.F18);
// glfwToTeraMaps.put(KeyEvent.VK_KANA, Keyboard.KeyId.KANA);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_F19, Keyboard.KeyId.F19);
// glfwToTeraMaps.put(KeyEvent.VK_CONVERT, Keyboard.KeyId.CONVERT);
// glfwToTeraMaps.put(KeyEvent.VK_NOCONVERT, Keyboard.KeyId.NOCONVERT);
// glfwToTeraMaps.put(KeyEvent.VK_YEN, Keyboard.KeyId.YEN);
// glfwToTeraMaps.put(KeyEvent.VK_CIRCUMFLEX, Keyboard.KeyId.CIRCUMFLEX);
// glfwToTeraMaps.put(KeyEvent.VK_AT, Keyboard.KeyId.AT);
// glfwToTeraMaps.put(KeyEvent.VK_COLON, Keyboard.KeyId.COLON);
// glfwToTeraMaps.put(KeyEvent.VK_UNDERLINE, Keyboard.KeyId.UNDERLINE);
// glfwToTeraMaps.put(KeyEvent.VK_KANJI, Keyboard.KeyId.KANJI);
// glfwToTeraMaps.put(KeyEvent.VK_STOP, Keyboard.KeyId.STOP);
// glfwToTeraMaps.put(KeyEvent.VK_AX, Keyboard.KeyId.AX);
// glfwToTeraMaps.put(KeyEvent.VK_UNLABELED, Keyboard.KeyId.UNLABELED);
// glfwToTeraMaps.put(KeyEvent.VK_SECTION, Keyboard.KeyId.SECTION);
// glfwToTeraMaps.put(KeyEvent.VK_KP_COMMA, Keyboard.KeyId.NUMPAD_COMMA);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DIVIDE, Keyboard.KeyId.NUMPAD_DIVIDE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_PRINTSCREEN, Keyboard.KeyId.PRINT_SCREEN);
// glfwToTeraMaps.put(KeyEvent.VK_FUNCTION, Keyboard.KeyId.FUNCTION);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_PAUSE, Keyboard.KeyId.PAUSE);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_HOME, Keyboard.KeyId.HOME);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_UP, Keyboard.KeyId.UP);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_PAGE_UP, Keyboard.KeyId.PAGE_UP);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_LEFT, Keyboard.KeyId.LEFT);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_RIGHT, Keyboard.KeyId.RIGHT);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_END, Keyboard.KeyId.END);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DOWN, Keyboard.KeyId.DOWN);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_PAGE_DOWN, Keyboard.KeyId.PAGE_DOWN);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_INSERT, Keyboard.KeyId.INSERT);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_DELETE, Keyboard.KeyId.DELETE);
// glfwToTeraMaps.put(KeyEvent.VK_CLEAR, Keyboard.KeyId.CLEAR);
AWT_TO_TERA_MAPPING.put(KeyEvent.VK_META, Keyboard.KeyId.LEFT_META);
// AWT_TO_TERA_MAPPING.put(KeyEvent.VK_RIGHT_SUPER, Keyboard.KeyId.RIGHT_META);
// glfwToTeraMaps.put(KeyEvent.VK_APPS, Keyboard.KeyId.APPS);
// glfwToTeraMaps.put(KeyEvent.VK_POWER, Keyboard.KeyId.POWER);
// glfwToTeraMaps.put(KeyEvent.VK_SLEEP, Keyboard.KeyId.SLEEP);
TIntIntHashMap controlMap = new TIntIntHashMap();
controlMap.put(KeyEvent.KEY_LOCATION_LEFT, Keyboard.KeyId.LEFT_CTRL);
controlMap.put(KeyEvent.KEY_LOCATION_RIGHT, Keyboard.KeyId.RIGHT_CTRL);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_CONTROL, controlMap);
TIntIntHashMap shiftMap = new TIntIntHashMap();
shiftMap.put(KeyEvent.KEY_LOCATION_LEFT, Keyboard.KeyId.LEFT_SHIFT);
shiftMap.put(KeyEvent.KEY_LOCATION_RIGHT, Keyboard.KeyId.RIGHT_SHIFT);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_SHIFT, shiftMap);
TIntIntHashMap altMap = new TIntIntHashMap();
altMap.put(KeyEvent.KEY_LOCATION_LEFT, Keyboard.KeyId.LEFT_ALT);
altMap.put(KeyEvent.KEY_LOCATION_RIGHT, Keyboard.KeyId.RIGHT_ALT);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_ALT, altMap);
TIntIntHashMap metaMap = new TIntIntHashMap();
metaMap.put(KeyEvent.KEY_LOCATION_LEFT, Keyboard.KeyId.LEFT_META);
metaMap.put(KeyEvent.KEY_LOCATION_RIGHT, Keyboard.KeyId.RIGHT_META);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_META, metaMap);
TIntIntHashMap equalsMap = new TIntIntHashMap();
equalsMap.put(KeyEvent.KEY_LOCATION_NUMPAD, Keyboard.KeyId.NUMPAD_EQUALS);
equalsMap.put(KeyEvent.KEY_LOCATION_STANDARD, Keyboard.KeyId.EQUALS);
equalsMap.put(KeyEvent.KEY_LOCATION_UNKNOWN, Keyboard.KeyId.EQUALS);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_EQUALS, equalsMap);
TIntIntHashMap enterMap = new TIntIntHashMap();
enterMap.put(KeyEvent.KEY_LOCATION_NUMPAD, Keyboard.KeyId.NUMPAD_ENTER);
enterMap.put(KeyEvent.KEY_LOCATION_STANDARD, Keyboard.KeyId.ENTER);
enterMap.put(KeyEvent.KEY_LOCATION_UNKNOWN, Keyboard.KeyId.ENTER);
AWT_TO_TERA_EXTRA.put(KeyEvent.VK_ENTER, enterMap);
}
private Queue<RawKeyboardAction> rawKeyQueue = Lists.newLinkedList();
private Queue<CharKeyboardAction> charQueue = Lists.newLinkedList();
private TIntSet buttonStates = new TIntHashSet();
public AwtKeyboardDevice() {
}
public void registerToAwtGlCanvas(AWTGLCanvas canvas) {
canvas.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
charQueue.offer(new CharKeyboardAction(e.getKeyChar()));
}
@Override
public void keyPressed(KeyEvent e) {
awtKeyCallback(e.getExtendedKeyCode(), ButtonState.DOWN, e.getKeyLocation());
}
@Override
public void keyReleased(KeyEvent e) {
awtKeyCallback(e.getExtendedKeyCode(), ButtonState.UP, e.getKeyLocation());
}
});
}
/**
* Callback receive key input events.
*/
public void awtKeyCallback(int key, ButtonState state, int location) {
int teraKey;
TIntIntHashMap extraMap = AWT_TO_TERA_EXTRA.get(key);
if (extraMap != null) {
teraKey = extraMap.get(key);
} else {
teraKey = AWT_TO_TERA_MAPPING.get(key);
}
Input input = InputType.KEY.getInput(teraKey);
if (state == ButtonState.DOWN) {
buttonStates.add(teraKey);
} else if (state == ButtonState.UP) {
buttonStates.remove(teraKey);
}
rawKeyQueue.offer(new RawKeyboardAction(input, state));
}
@Override
public boolean isKeyDown(int key) {
return buttonStates.contains(key);
}
@Override
public Queue<RawKeyboardAction> getInputQueue() {
Queue<RawKeyboardAction> rawKeyboardActions = Lists.newLinkedList();
RawKeyboardAction action;
while ((action = rawKeyQueue.poll()) != null) {
rawKeyboardActions.add(action);
}
return rawKeyboardActions;
}
@Override
public Queue<CharKeyboardAction> getCharInputQueue() {
Queue<CharKeyboardAction> charActions = Lists.newLinkedList();
CharKeyboardAction action;
while ((action = charQueue.poll()) != null) {
charActions.add(action);
}
return charActions;
}
}

View file

@ -1,164 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.input;
import com.google.common.collect.Lists;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.joml.Vector2d;
import org.joml.Vector2i;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.terasology.engine.config.RenderingConfig;
import org.terasology.input.ButtonState;
import org.terasology.input.InputType;
import org.terasology.input.MouseInput;
import org.terasology.input.device.MouseAction;
import org.terasology.input.device.MouseDevice;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Queue;
/**
* Awt mouse device convertor. Handles mouse input via AWT's callbacks Handles mouse state.
*/
public class AwtMouseDevice implements MouseDevice, PropertyChangeListener {
private RenderingConfig renderingConfig;
private float uiScale;
private boolean mouseGrabbed;
private Queue<MouseAction> queue = Lists.newLinkedList();
private TIntSet buttonStates = new TIntHashSet();
private double xPos;
private double yPos;
private double xPosDelta;
private double yPosDelta;
public AwtMouseDevice(RenderingConfig renderingConfig) {
this.renderingConfig = renderingConfig;
this.uiScale = renderingConfig.getUiScale() / 100f;
renderingConfig.subscribe(RenderingConfig.UI_SCALE, this);
}
public void registerToAwtGlCanvas(AWTGLCanvas canvas) {
canvas.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
int button = e.getButton() - 1;
buttonStates.add(button);
MouseInput mouseInput = MouseInput.find(InputType.MOUSE_BUTTON, button);
queue.offer(new MouseAction(mouseInput, ButtonState.DOWN, getPosition()));
}
@Override
public void mouseReleased(MouseEvent e) {
int button = e.getButton() - 1;
buttonStates.remove(button);
MouseInput mouseInput = MouseInput.find(InputType.MOUSE_BUTTON, button);
queue.offer(new MouseAction(mouseInput, ButtonState.UP, getPosition()));
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
canvas.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
updateMouse(e.getX(), e.getY());
}
@Override
public void mouseMoved(MouseEvent e) {
updateMouse(e.getX(), e.getY());
}
});
canvas.addMouseWheelListener(e -> {
int yOffset = e.getUnitsToScroll();
if (yOffset != 0.0) {
int id = (yOffset > 0) ? 1 : -1;
queue.offer(new MouseAction(InputType.MOUSE_WHEEL.getInput(id), 1, getPosition()));
}
});
}
@Override
public void update() {
}
private void updateMouse(double x, double y) {
xPosDelta = x - this.xPos;
yPosDelta = y - this.yPos;
this.xPos = x;
this.yPos = y;
}
@Override
public Vector2i getPosition() {
return new Vector2i((int) (xPos / this.uiScale), (int) (yPos / this.uiScale));
}
@Override
public Vector2d getDelta() {
Vector2d result = new Vector2d(xPosDelta, yPosDelta);
return result;
}
@Override
public boolean isButtonDown(int button) {
return buttonStates.contains(button);
}
@Override
public boolean isVisible() {
return !mouseGrabbed;
}
@Override
public void setGrabbed(boolean newGrabbed) {
if (newGrabbed != mouseGrabbed) {
mouseGrabbed = newGrabbed;
// TODO handle swing mouse grabbing
}
}
@Override
public Queue<MouseAction> getInputQueue() {
Queue<MouseAction> mouseActions = Lists.newLinkedList();
MouseAction action;
while ((action = queue.poll()) != null) {
mouseActions.add(action);
}
return mouseActions;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(RenderingConfig.UI_SCALE)) {
this.uiScale = this.renderingConfig.getUiScale() / 100f;
}
}
public void resetDelta() {
xPosDelta = 0;
yPosDelta = 0;
}
}

View file

@ -1,58 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.properties;
import org.terasology.reflection.metadata.FieldMetadata;
import java.text.DecimalFormat;
public class FloatProperty<T> implements Property<Float> {
private static final DecimalFormat DEFAULT_DECIMAL_FORMAT = new DecimalFormat("0.0000000");
private final float min;
private final float max;
private final T target;
private final FieldMetadata<T, Float> field;
public FloatProperty(T target, FieldMetadata<T, Float> field, float min, float max) {
this.target = target;
this.min = min;
this.max = max;
this.field = field;
}
public float getMinValue() {
return min;
}
public float getMaxValue() {
return max;
}
@Override
public Float getValue() {
return field.getValueChecked(target);
}
@Override
public void setValue(Float value) {
field.setValue(target, value);
}
@Override
public Class<Float> getValueType() {
return Float.class;
}
@Override
public String getTitle() {
return field.getName();
}
@Override
public String toString() {
return DEFAULT_DECIMAL_FORMAT.format(getValue());
}
}

View file

@ -1,14 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.properties;
public interface Property<T> {
T getValue();
void setValue(T value);
Class<T> getValueType();
String getTitle();
}

View file

@ -1,14 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.properties;
import java.util.List;
public interface PropertyProvider<T> {
/**
* Adds the properties of this Object to the given property list.
*
* @return a list of the properties of this object
*/
List<Property<T>> getProperties();
}

View file

@ -1,53 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.properties;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.engine.context.Context;
import org.terasology.nui.properties.Range;
import org.terasology.reflection.copy.CopyStrategyLibrary;
import org.terasology.reflection.metadata.ClassMetadata;
import org.terasology.reflection.metadata.DefaultClassMetadata;
import org.terasology.reflection.metadata.FieldMetadata;
import org.terasology.reflection.reflect.ReflectFactory;
import java.lang.reflect.Field;
import java.util.List;
import static com.google.common.base.Predicates.and;
import static com.google.common.base.Predicates.or;
import static org.reflections.ReflectionUtils.getAllFields;
import static org.reflections.ReflectionUtils.withAnnotation;
import static org.reflections.ReflectionUtils.withType;
public class ReflectionProvider<T> implements PropertyProvider<T> {
private static final Logger logger = LoggerFactory.getLogger(ReflectionProvider.class);
private List<Property<T>> properties = Lists.newArrayList();
public ReflectionProvider(T target, Context context) {
try {
ReflectFactory reflectFactory = context.get(ReflectFactory.class);
CopyStrategyLibrary copyStrategies = context.get(CopyStrategyLibrary.class);
ClassMetadata<T, ?> classMetadata = new DefaultClassMetadata<>("engine:empty", (Class<T>) target.getClass(),
reflectFactory, copyStrategies);
for (Field field : getAllFields(target.getClass(),
and(withAnnotation(Range.class), or(withType(Float.TYPE), withType(Float.class))))) {
Range range = field.getAnnotation(Range.class);
FieldMetadata<T, Float> fieldMetadata = (FieldMetadata<T, Float>) classMetadata.getField(field.getName());
Property property = new FloatProperty(target, fieldMetadata, range.min(), range.max());
properties.add(property);
}
} catch (NoSuchMethodException e) {
logger.error("Cannot provide provide inspection for {}, does not have a default constructor", target.getClass());
}
}
@Override
public List<Property<T>> getProperties() {
return properties;
}
}

View file

@ -1,45 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.properties;
import com.google.common.collect.Lists;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.TerasologyEngine;
import org.terasology.engine.core.modes.GameState;
import org.terasology.engine.core.modes.StateIngame;
import org.terasology.engine.rendering.backdrop.BackdropProvider;
import java.util.List;
public class SceneProperties implements PropertyProvider {
private final TerasologyEngine engine;
public SceneProperties(TerasologyEngine engine) {
this.engine = engine;
}
@Override
public List<Property<?>> getProperties() {
List<Property<?>> result = Lists.newArrayList();
GameState gameState = engine.getState();
if (!(gameState instanceof StateIngame)) {
return result;
}
StateIngame ingameState = (StateIngame) gameState;
Context ingameContext = ingameState.getContext();
BackdropProvider backdropProvider = ingameContext.get(BackdropProvider.class);
if (backdropProvider != null) {
result.addAll(new ReflectionProvider(backdropProvider, ingameContext).getProperties());
}
// TODO: fix this
/*FrameBuffersManager renderingProcess = ingameContext.get(FrameBuffersManager.class);
if (renderingProcess != null) {
result.addAll(new ReflectionProvider(renderingProcess, ingameContext).getProperties());
}*/
return result;
}
}

View file

@ -1,60 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.subsystem;
import org.terasology.gestalt.assets.module.ModuleAwareAssetTypeManager;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.ControllerConfig;
import org.terasology.engine.context.Context;
import org.terasology.editor.input.AwtKeyboardDevice;
import org.terasology.editor.input.AwtMouseDevice;
import org.terasology.engine.core.modes.GameState;
import org.terasology.engine.core.subsystem.config.BindsManager;
import org.terasology.engine.core.subsystem.lwjgl.BaseLwjglSubsystem;
import org.terasology.engine.input.InputSystem;
import org.terasology.engine.input.lwjgl.LwjglControllerDevice;
public class AwtInput extends BaseLwjglSubsystem {
private Context context;
@Override
public String getName() {
return "Input";
}
@Override
public void registerCoreAssetTypes(ModuleAwareAssetTypeManager assetTypeManager) {
}
@Override
public void postInitialise(Context rootContext) {
this.context = rootContext;
initControls();
updateInputConfig();
}
@Override
public void postUpdate(GameState currentState, float delta) {
currentState.handleInput(delta);
}
private void initControls() {
Config config = context.get(Config.class);
InputSystem inputSystem = new InputSystem();
context.put(InputSystem.class, inputSystem);
inputSystem.setMouseDevice(new AwtMouseDevice(config.getRendering()));
inputSystem.setKeyboardDevice(new AwtKeyboardDevice());
ControllerConfig controllerConfig = config.getInput().getControllers();
LwjglControllerDevice controllerDevice = new LwjglControllerDevice(controllerConfig);
inputSystem.setControllerDevice(controllerDevice);
}
private void updateInputConfig() {
BindsManager bindsManager = context.get(BindsManager.class);
bindsManager.updateConfigWithDefaultBinds();
bindsManager.saveBindsConfig();
}
}

View file

@ -1,219 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.subsystem;
import com.google.common.base.Preconditions;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.opengl.GL43;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.lwjgl.opengl.awt.GLData;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.gestalt.assets.module.ModuleAwareAssetTypeManager;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.RenderingConfig;
import org.terasology.engine.context.Context;
import org.terasology.editor.input.AwtKeyboardDevice;
import org.terasology.editor.input.AwtMouseDevice;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.GameThread;
import org.terasology.engine.core.TerasologyEngine;
import org.terasology.engine.core.modes.GameState;
import org.terasology.engine.core.subsystem.DisplayDevice;
import org.terasology.engine.core.subsystem.lwjgl.BaseLwjglSubsystem;
import org.terasology.engine.core.subsystem.lwjgl.DebugCallback;
import org.terasology.engine.core.subsystem.lwjgl.GLFWErrorCallback;
import org.terasology.engine.core.subsystem.lwjgl.LwjglGraphicsManager;
import org.terasology.engine.core.subsystem.lwjgl.LwjglGraphicsUtil;
import org.terasology.engine.entitySystem.event.internal.EventSystem;
import org.terasology.engine.input.InputSystem;
import org.terasology.nui.canvas.CanvasRenderer;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.ShaderManager;
import org.terasology.engine.rendering.ShaderManagerLwjgl;
import org.terasology.engine.rendering.nui.internal.LwjglCanvasRenderer;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
public class LwjglPortlet extends BaseLwjglSubsystem {
private static final Logger logger = LoggerFactory.getLogger(LwjglPortlet.class);
private Context context;
private RenderingConfig config;
private GameEngine engine;
private AWTGLCanvas canvas;
private LwjglPortletDisplayDevice display;
private AwtMouseDevice mouseDevice;
private final LwjglGraphicsManager graphics = new LwjglGraphicsManager();
@Override
public String getName() {
return "Portlet";
}
@Override
public void initialise(GameEngine gameEngine, Context rootContext) {
logger.info("Starting initialization of LWJGL");
this.engine = gameEngine;
this.context = rootContext;
this.config = context.get(Config.class).getRendering();
graphics.setThreadMode(LwjglGraphicsManager.ThreadMode.DISPLAY_THREAD);
display = new LwjglPortletDisplayDevice(canvas, graphics);
context.put(DisplayDevice.class, display);
logger.info("Initial initialization complete");
}
@Override
public void registerCoreAssetTypes(ModuleAwareAssetTypeManager assetTypeManager) {
graphics.registerCoreAssetTypes(assetTypeManager);
}
@Override
public void postInitialise(Context rootContext) {
graphics.registerRenderingSubsystem(context);
initBuffer();
context.put(ShaderManager.class, new ShaderManagerLwjgl());
context.put(CanvasRenderer.class, new LwjglCanvasRenderer(context));
}
@Override
public void postUpdate(GameState currentState, float delta) {
graphics.processActions();
currentState.render();
display.update();
int frameLimit = context.get(Config.class).getRendering().getFrameLimit();
// TODO: do we still need this?
// if (frameLimit > 0) {
// Lwjgl2Sync.sync(frameLimit);
// }
if (display.isCloseRequested()) {
engine.shutdown();
}
}
public void setupThreads() {
GameThread.reset();
GameThread.setToCurrentThread();
graphics.setThreadMode(LwjglGraphicsManager.ThreadMode.GAME_THREAD);
EventSystem eventSystem = CoreRegistry.get(EventSystem.class);
if (eventSystem != null) {
eventSystem.setToCurrentThread();
}
}
public void createCanvas() {
GLData data = new GLData();
data.samples = 4;
canvas = new AWTGLCanvas() {
@Override
public void initGL() {
initGLFW();
initOpenGL();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
}
@Override
public void paintGL() {
if (((TerasologyEngine) engine).tick()) {
mouseDevice.resetDelta();
}
}
};
}
public AWTGLCanvas getCanvas() {
return this.canvas;
}
private void initGLFW() {
if (!GLFW.glfwInit()) {
throw new RuntimeException("Failed to initialize GLFW");
}
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_COCOA_GRAPHICS_SWITCHING, GLFW.GLFW_TRUE);
GLFW.glfwWindowHint(GLFW.GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_DEPTH_BITS, config.getPixelFormat());
if (config.getDebug().isEnabled()) {
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE);
}
GLFW.glfwSetErrorCallback(new GLFWErrorCallback());
}
private void initBuffer() {
logger.info("Initializing display (if last line in log then likely the game crashed from an issue with your " +
"video card)");
if (!config.isVSync()) {
GLFW.glfwSwapInterval(0);
}
try {
String root = "org/terasology/icons/";
ClassLoader classLoader = getClass().getClassLoader();
BufferedImage icon16 = ImageIO.read(classLoader.getResourceAsStream(root + "gooey_sweet_16.png"));
BufferedImage icon32 = ImageIO.read(classLoader.getResourceAsStream(root + "gooey_sweet_32.png"));
BufferedImage icon64 = ImageIO.read(classLoader.getResourceAsStream(root + "gooey_sweet_64.png"));
BufferedImage icon128 = ImageIO.read(classLoader.getResourceAsStream(root + "gooey_sweet_128.png"));
GLFWImage.Buffer buffer = GLFWImage.create(4);
buffer.put(0, LwjglGraphicsUtil.convertToGLFWFormat(icon16));
buffer.put(1, LwjglGraphicsUtil.convertToGLFWFormat(icon32));
buffer.put(2, LwjglGraphicsUtil.convertToGLFWFormat(icon64));
buffer.put(3, LwjglGraphicsUtil.convertToGLFWFormat(icon128));
} catch (IOException | IllegalArgumentException e) {
logger.warn("Could not set icon", e);
}
display.setDisplayModeSetting(config.getDisplayModeSetting());
}
private void initOpenGL() {
logger.info("Initializing OpenGL");
LwjglGraphicsUtil.checkOpenGL();
LwjglGraphicsUtil.initOpenGLParams();
if (config.getDebug().isEnabled()) {
try {
GL43.glDebugMessageCallback(new DebugCallback(), MemoryUtil.NULL);
} catch (IllegalStateException e) {
logger.warn("Unable to specify DebugCallback to receive debugging messages from the GL.");
}
}
}
public void initInputs() {
final InputSystem inputSystem = context.get(InputSystem.class);
Preconditions.checkNotNull(inputSystem);
mouseDevice = ((AwtMouseDevice) inputSystem.getMouseDevice());
mouseDevice.registerToAwtGlCanvas(canvas);
((AwtKeyboardDevice) inputSystem.getKeyboard()).registerToAwtGlCanvas(canvas);
}
@Override
public void shutdown() {
GLFW.glfwTerminate();
}
}

View file

@ -1,130 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.subsystem;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.terasology.engine.core.subsystem.DisplayDevice;
import org.terasology.engine.core.subsystem.DisplayDeviceInfo;
import org.terasology.engine.core.subsystem.Resolution;
import org.terasology.engine.core.subsystem.lwjgl.LwjglDisplayDevice;
import org.terasology.engine.core.subsystem.lwjgl.LwjglGraphicsManager;
import org.terasology.engine.core.subsystem.lwjgl.LwjglResolution;
import org.terasology.engine.rendering.nui.layers.mainMenu.videoSettings.DisplayModeSetting;
import org.terasology.engine.utilities.subscribables.AbstractSubscribable;
import java.awt.GraphicsEnvironment;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
public class LwjglPortletDisplayDevice extends AbstractSubscribable implements DisplayDevice {
private final AWTGLCanvas canvas;
private final LwjglGraphicsManager graphics;
public LwjglPortletDisplayDevice(AWTGLCanvas canvas, LwjglGraphicsManager graphics) {
this.canvas = canvas;
this.graphics = graphics;
canvas.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updateViewport();
}
});
}
@Override
public boolean hasFocus() {
return canvas.hasFocus();
}
@Override
public boolean isCloseRequested() {
return false;
}
@Override
public void setFullscreen(boolean state) {
}
@Override
public boolean isFullscreen() {
return false;
}
@Override
public void setDisplayModeSetting(DisplayModeSetting displayModeSetting) {
}
@Override
public DisplayModeSetting getDisplayModeSetting() {
return DisplayModeSetting.WINDOWED;
}
@Override
public Resolution getResolution() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
int bitDepth = env.getDefaultScreenDevice().getDisplayMode().getBitDepth();
int refreshRate = env.getDefaultScreenDevice().getDisplayMode().getRefreshRate();
return new LwjglResolution(getWidth(), getHeight(), bitDepth, bitDepth, bitDepth, refreshRate);
}
@Override
public List<Resolution> getResolutions() {
ArrayList<Resolution> resolutions = new ArrayList<>();
resolutions.add(getResolution());
return resolutions;
}
@Override
public int getWidth() {
return canvas.getWidth();
}
@Override
public int getHeight() {
return canvas.getHeight();
}
@Override
public void setResolution(Resolution resolution) {
}
@Override
public void processMessages() {
}
@Override
public boolean isHeadless() {
return false;
}
@Override
public void prepareToRender() {
}
private void updateViewport() {
updateViewport(getWidth(), getHeight());
}
protected void updateViewport(int width, int height) {
graphics.asynchToDisplayThread(() -> {
GL11.glViewport(0, 0, width, height);
propertyChangeSupport.firePropertyChange(LwjglDisplayDevice.DISPLAY_RESOLUTION_CHANGE, 0, 1);
});
}
@Override
public void update() {
processMessages();
canvas.swapBuffers();
}
@Override
public DisplayDeviceInfo getInfo() {
return graphics.getDisplayDeviceInfo();
}
}

View file

@ -1,183 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.ui;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.editor.TeraEd;
import org.terasology.engine.core.StateChangeSubscriber;
import org.terasology.engine.core.TerasologyEngine;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.ScrollPaneConstants;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
/**
* TeraEd main class.
*/
@SuppressWarnings("serial")
public final class MainWindow extends JFrame implements ActionListener, WindowListener, StateChangeSubscriber {
private static final Logger logger = LoggerFactory.getLogger(MainWindow.class);
private TeraEd teraEd;
private TerasologyEngine engine;
private BorderLayout borderLayout;
private Viewport viewport;
private PropertyPanel propertyPanel;
private JSplitPane verticalSplitPane;
private JMenuBar mainMenuBar;
private JMenu fileMenu;
private JMenuItem fileMenuExitItem;
private JMenu shaderPropertiesMenu;
private java.util.List<JMenuItem> shaderPropertyMenuEntries = new ArrayList<>(64);
private JMenu propertiesMenu;
private JMenuItem propertiesMenuScene;
private JScrollPane propertyPanelScrollPane;
public MainWindow(TeraEd teraEd, TerasologyEngine engine) {
this.teraEd = teraEd;
this.addWindowListener(this);
this.engine = engine;
viewport = new Viewport();
borderLayout = new BorderLayout();
getContentPane().setLayout(borderLayout);
// Build up the main window editor layout...
propertyPanel = new PropertyPanel();
propertyPanelScrollPane = new JScrollPane(
propertyPanel,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
propertyPanelScrollPane.setMinimumSize(new Dimension(350, 720));
propertyPanelScrollPane.setPreferredSize(new Dimension(350, 720));
verticalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewport, propertyPanelScrollPane);
verticalSplitPane.setContinuousLayout(true);
verticalSplitPane.setResizeWeight(0.5);
getContentPane().add(verticalSplitPane, BorderLayout.CENTER);
setTitle("TeraEd - Terasology" + " | " + "Alpha");
mainMenuBar = new JMenuBar();
setJMenuBar(mainMenuBar);
fileMenu = new JMenu("File");
fileMenuExitItem = new JMenuItem("Exit");
fileMenuExitItem.addActionListener(this);
fileMenu.add(fileMenuExitItem);
shaderPropertiesMenu = new JMenu("Shader Properties");
propertiesMenu = new JMenu("Properties");
propertiesMenuScene = new JMenuItem("Scene");
propertiesMenuScene.addActionListener(this);
propertiesMenu.add(propertiesMenuScene);
mainMenuBar.add(fileMenu);
mainMenuBar.add(shaderPropertiesMenu);
mainMenuBar.add(propertiesMenu);
pack();
setVisible(true);
}
public Viewport getViewport() {
return viewport;
}
public void onStateChange() {
/*
shaderPropertyMenuEntries.clear();
shaderPropertiesMenu.removeAll();
GameState gameState = engine.getState();
if (gameState instanceof StateIngame) {
StateIngame stateIngame = (StateIngame) gameState;
Context ingameContext = stateIngame.getContext();
AssetManager assetManager = ingameContext.get(AssetManager.class);
for (Material material : assetManager.getLoadedAssets(Material.class)) {
GLSLMaterial finalMat = (GLSLMaterial) material;
if (finalMat.getShaderParameters() != null) {
final PropertyProvider provider = new ReflectionProvider(finalMat.getShaderParameters(),
ingameContext);
if (!provider.getProperties().isEmpty()) {
final String programName = material.getUrn().toString();
JMenuItem menuItem = new JMenuItem(programName);
menuItem.addActionListener(e -> {
propertyPanel.setActivePropertyProvider(provider);
propertyPanel.setTitle(programName);
});
shaderPropertyMenuEntries.add(menuItem);
shaderPropertiesMenu.add(menuItem);
}
}
}
}
*/
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fileMenuExitItem) {
teraEd.getEngine().shutdown();
} else if (e.getSource() == propertiesMenuScene) {
propertyPanel.setActivePropertyProvider(teraEd.getSceneProperties());
propertyPanel.setTitle("Scene Properties");
}
}
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
teraEd.getEngine().shutdown();
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
}

View file

@ -1,72 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.ui;
import org.terasology.editor.properties.FloatProperty;
import org.terasology.editor.properties.Property;
import org.terasology.editor.properties.PropertyProvider;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.util.Iterator;
import java.util.List;
public class PropertyPanel extends JPanel {
private static final long serialVersionUID = 6844552770055484579L;
private PropertyProvider activePropertyProvider;
private TitledBorder border;
private String title = "";
public PropertyPanel() {
border = new TitledBorder("");
setBorder(border);
}
public PropertyPanel(String title) {
this();
this.title = title;
border.setTitle(title);
}
public PropertyPanel(PropertyProvider provider) {
this();
setActivePropertyProvider(provider);
}
public void setActivePropertyProvider(PropertyProvider provider) {
activePropertyProvider = provider;
onActivePropertyProviderChanged();
}
public void onActivePropertyProviderChanged() {
removeAll();
if (activePropertyProvider != null) {
List<Property<?>> properties = activePropertyProvider.getProperties();
setLayout(new GridLayout(properties.size() >= 16 ? properties.size() : 16, 1));
Iterator<Property<?>> it = properties.iterator();
while (it.hasNext()) {
Property property = it.next();
if (property instanceof FloatProperty) {
add(new PropertySlider((FloatProperty) property));
revalidate();
}
}
}
repaint();
}
public void setTitle(String title) {
this.title = title;
border.setTitle(title);
revalidate();
}
}

View file

@ -1,77 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.ui;
import org.terasology.editor.properties.FloatProperty;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
public class PropertySlider extends JPanel implements ChangeListener {
private static final long serialVersionUID = 3157887601371629996L;
private JSlider slider;
private FloatProperty activeProperty;
private BorderLayout borderLayout;
private TitledBorder titledBorder;
private JLabel label;
public PropertySlider() {
titledBorder = new TitledBorder("");
setBorder(titledBorder);
borderLayout = new BorderLayout();
setLayout(borderLayout);
label = new JLabel("");
slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(100);
slider.setMinorTickSpacing(1);
slider.setMajorTickSpacing(10);
slider.addChangeListener(this);
add(slider, BorderLayout.CENTER);
add(label, BorderLayout.EAST);
}
public PropertySlider(FloatProperty property) {
this();
setActiveProperty(property);
}
public void setActiveProperty(FloatProperty provider) {
activeProperty = provider;
onActivePropertyChanged();
}
public void onActivePropertyChanged() {
if (activeProperty != null) {
titledBorder.setTitle(activeProperty.getTitle());
if (activeProperty.getValueType() == Float.class) {
setValue(activeProperty.getValue(), activeProperty.getMinValue(), activeProperty.getMaxValue());
}
}
}
public void setValue(float value, float minValue, float maxValue) {
int sliderValue = (int) (((value - minValue) / (maxValue - minValue)) * 100.0f);
slider.setValue(sliderValue);
}
@Override
public void stateChanged(ChangeEvent e) {
if (activeProperty.getValueType() == Float.class) {
float range = Math.abs(activeProperty.getMaxValue() - activeProperty.getMinValue());
float val = (slider.getValue() / 100.0f) * range + activeProperty.getMinValue();
activeProperty.setValue(val);
label.setText(activeProperty.toString());
}
}
}

View file

@ -1,39 +0,0 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.editor.ui;
import org.lwjgl.opengl.awt.AWTGLCanvas;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import java.awt.BorderLayout;
import java.awt.Dimension;
/**
* TeraEd main class.
*/
@SuppressWarnings("serial")
public final class Viewport extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(Viewport.class);
private JLabel startingLabel = new JLabel("Starting Terasology...");
public Viewport() {
setLayout(new BorderLayout());
setSize(1280, 720);
setMinimumSize(new Dimension(640, 480));
setPreferredSize(new Dimension(1280, 720));
setOpaque(false);
startingLabel.setHorizontalAlignment(SwingConstants.CENTER);
add(startingLabel, BorderLayout.CENTER);
}
public void setTerasology(AWTGLCanvas canvas) {
remove(startingLabel);
add(canvas, BorderLayout.CENTER);
revalidate();
}
}

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Report all changes to this configuration on System.out -->
<!--statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /-->
<!-- Propagate all log level changes to java.util.logging -->
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="${logOverrideLevel:-debug}">
<appender-ref ref="CONSOLE" />
</root>
<logger name="org.terasology" level="${logOverrideLevel:-info}" />
<logger name="org.reflections.Reflections" level="${logOverrideLevel:-warn}" />
</configuration>

View file

@ -62,13 +62,18 @@ private PersistedData serializeEntry(Map.Entry<K, V> entry, PersistedDataSeriali
@Override
public Optional<Map<K, V>> deserialize(PersistedData data) {
Map<K, V> result = Maps.newLinkedHashMap();
if (data.isNull()) {
return Optional.of(result);
}
if (!data.isArray() || data.isValueMap()) {
logger.warn("Incorrect map format detected: object instead of array.\n" + getUsageInfo(data));
return Optional.empty();
}
Map<K, V> result = Maps.newLinkedHashMap();
for (PersistedData entry : data.getAsArray()) {
PersistedDataMap kvEntry = entry.getAsValueMap();
PersistedData rawKey = kvEntry.get(KEY);
@ -101,7 +106,7 @@ private String getUsageInfo(PersistedData data) {
" \"mapName\": [\n" +
" { \"key\": \"...\", \"value\": \"...\" }\n" +
" ]\n" +
"but found \n'{}'" + data + "'";
"but found \n'" + data + "'";
}
@Override

View file

@ -4,16 +4,23 @@
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.terasology.persistence.typeHandling.PersistedData;
import org.terasology.persistence.typeHandling.PersistedDataSerializer;
import org.terasology.persistence.typeHandling.inMemory.arrays.PersistedIntegerArray;
import org.terasology.persistence.typeHandling.inMemory.arrays.PersistedValueArray;
import org.terasology.reflection.TypeInfo;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
@ -65,4 +72,20 @@ void testDeserialize() {
verify(elementTypeHandler, times(intList.size())).deserialize(any());
}
@Test
@DisplayName("An empty array encoded as '[]' can be deserialized successfully.")
void testDeserializeEmpty() {
ArrayTypeHandler<String> typeHandler = new ArrayTypeHandler<>(
new StringTypeHandler(),
TypeInfo.of(String.class)
);
var testData = new PersistedValueArray(List.of());
var result = typeHandler.deserialize(testData);
assertThat(result).isPresent();
assertThat(Array.getLength(result.get())).isEqualTo(0);
}
}

View file

@ -6,18 +6,27 @@
import com.google.common.collect.Queues;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.stubbing.Answer;
import org.terasology.persistence.typeHandling.PersistedData;
import org.terasology.persistence.typeHandling.PersistedDataSerializer;
import org.terasology.persistence.typeHandling.inMemory.arrays.PersistedIntegerArray;
import org.terasology.persistence.typeHandling.inMemory.arrays.PersistedValueArray;
import org.terasology.reflection.TypeInfo;
import org.terasology.reflection.reflect.CollectionCopyConstructor;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
@ -78,4 +87,20 @@ void testDeserialize() {
verify(elementTypeHandler, times(intList.size())).deserialize(any());
}
@Test
@DisplayName("An empty collection encoded as '[]' can be deserialized successfully.")
void testDeserializeEmpty() {
ArrayTypeHandler<String> typeHandler = new ArrayTypeHandler<>(
new StringTypeHandler(),
TypeInfo.of(String.class)
);
var testData = new PersistedValueArray(List.of());
var result = typeHandler.deserialize(testData);
assertThat(result).isPresent();
assertThat(Array.getLength(result.get())).isEqualTo(0);
}
}

View file

@ -119,6 +119,14 @@ GenericMapTypeHandler.VALUE, new PersistedLong(TEST_VALUE)
))
));
/**
* JSON equivalent:
* <pre><code>
* [ ]
* </code></pre>
*/
private final PersistedData testDataValidEmpty = new PersistedValueArray(List.of());
@Test
@DisplayName("Data with valid formatting can be deserialized successfully.")
void testDeserialize() {
@ -197,6 +205,17 @@ void testDeserializeWithValidAndInvalidEntries() {
assertThat(th.deserialize(testDataValidAndInvalidMix)).isEmpty();
}
@Test
@DisplayName("An empty map encoded as empty array '[]' can be deserialized successfully.")
void testDeserializeEmptyMap() {
var th = new GenericMapTypeHandler<>(new StringTypeHandler(), new LongTypeHandler());
var result = th.deserialize(testDataValidEmpty);
assertThat(result).isPresent();
assertThat(result.get()).isEmpty();
}
/** Never returns a value. */
private static class UselessTypeHandler<T> extends TypeHandler<T> {
@Override