mirror of
https://github.com/MovingBlocks/Terasology
synced 2026-05-24 09:28:22 +00:00
Merge branch 'develop' into java-18-disable-securitymanager
This commit is contained in:
commit
9cca631e77
52 changed files with 343 additions and 2809 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -16,7 +16,6 @@
|
|||
extensions
|
||||
/facades/*
|
||||
!/facades/PC
|
||||
!/facades/TeraEd
|
||||
!/facades/subprojects.gradle
|
||||
/modules/*
|
||||
!/modules/subprojects.gradle
|
||||
|
|
|
|||
6
.idea/kotlinc.xml
Normal file
6
.idea/kotlinc.xml
Normal 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>
|
||||
|
|
@ -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
4
Jenkinsfile
vendored
|
|
@ -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)
|
||||
])
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
class facade {
|
||||
|
||||
def excludedItems = ["PC", "TeraEd"]
|
||||
def excludedItems = ["PC"]
|
||||
|
||||
def getGithubDefaultHome(Properties properties) {
|
||||
return properties.alternativeGithubHome ?: "MovingBlocks"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ListOfObject" : {
|
||||
"shortName" : "Test",
|
||||
"elements" : [ ]
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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> {
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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ů",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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文字以上長くできません",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 символів",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
{
|
||||
"type": "UIBox",
|
||||
"layoutInfo": {
|
||||
"width": 200,
|
||||
"width": 400,
|
||||
"use-content-height": true,
|
||||
"position-horizontal-center": {},
|
||||
"position-vertical-center": {}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
11
facades/TeraEd/.gitignore
vendored
11
facades/TeraEd/.gitignore
vendored
|
|
@ -1,11 +0,0 @@
|
|||
# Boo eclipse! - every eclipse project needs its own .gitignore file
|
||||
|
||||
# Ignore Eclipse
|
||||
/.checkstyle
|
||||
/.project
|
||||
/.classpath
|
||||
/.settings/
|
||||
/bin/
|
||||
|
||||
# Ignore gradle
|
||||
/build/
|
||||
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue