From 1ef57ce5d578d91ef50c65d16ce6b0cf4e7028e2 Mon Sep 17 00:00:00 2001 From: Nail Khanipov Date: Fri, 2 Apr 2021 18:45:23 +0300 Subject: [PATCH] feat(player-config): migrate PlayerConfig to AutoConfig (#4599) --- .../flexible/ui/SettingWidgetFactoryTest.java | 4 +- .../org/terasology/engine/config/Config.java | 31 +-- .../engine/config/PlayerConfig.java | 135 ++++----- .../terasology/engine/config/RootConfig.java | 22 +- .../engine/config/SystemConfig.java | 22 +- .../config/flexible/AutoConfigManager.java | 4 +- .../flexible/bindings/MappingBinding.java | 30 ++ .../flexible/bindings/SettingBinding.java | 27 ++ .../flexible/constraints/ColorConstraint.java | 18 ++ .../constraints/LocaleConstraint.java | 42 +++ .../constraints/PredicateWithDescription.java | 25 ++ .../constraints/StringConstraint.java | 61 ++++ .../config/flexible/ui/AutoConfigScreen.java | 6 +- .../flexible/ui/AutoConfigWidgetFactory.java | 12 +- .../ui/ColorConstraintWidgetFactory.java | 87 ++++++ .../ui/DefaultConstraintWidgetFactory.java | 38 +++ .../ui/LocaleConstraintWidgetFactory.java | 76 +++++ .../flexible/ui/SettingWidgetFactory.java | 33 ++- .../modes/loadProcesses/SetupLocalPlayer.java | 23 +- .../engine/i18n/TranslationSystemImpl.java | 4 +- .../logic/characters/GazeAuthoritySystem.java | 6 +- .../logic/players/LocalPlayerSystem.java | 25 +- .../internal/ClientConnectionHandler.java | 12 +- .../layers/mainMenu/EnterUsernamePopup.java | 19 +- .../nui/layers/mainMenu/JoinGameScreen.java | 21 +- .../nui/layers/mainMenu/SelectGameScreen.java | 6 +- .../nui/layers/mainMenu/SelectionScreen.java | 18 +- .../settings/PlayerSettingsScreen.java | 261 +----------------- .../rendering/world/WorldRendererImpl.java | 17 +- .../metrics/GameConfigurationMetric.java | 23 +- .../src/main/resources/assets/i18n/menu.lang | 1 + .../main/resources/assets/i18n/menu_en.lang | 1 + .../assets/ui/config/autoConfigScreen.ui | 42 ++- .../assets/ui/config/colorPickerWidget.ui | 15 + .../assets/ui/menu/playerMenuScreen.ui | 88 ------ .../discordrpc/DiscordAutoConfig.java | 27 ++ .../discordrpc/DiscordRPCSubSystem.java | 127 ++++----- 37 files changed, 720 insertions(+), 689 deletions(-) create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/bindings/MappingBinding.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/bindings/SettingBinding.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/constraints/ColorConstraint.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/constraints/LocaleConstraint.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/constraints/PredicateWithDescription.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/constraints/StringConstraint.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/ui/ColorConstraintWidgetFactory.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/ui/DefaultConstraintWidgetFactory.java create mode 100644 engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java create mode 100644 engine/src/main/resources/assets/ui/config/colorPickerWidget.ui create mode 100644 subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordAutoConfig.java diff --git a/engine-tests/src/test/java/org/terasology/config/flexible/ui/SettingWidgetFactoryTest.java b/engine-tests/src/test/java/org/terasology/config/flexible/ui/SettingWidgetFactoryTest.java index 0a394a8a98..fcb936d6fe 100644 --- a/engine-tests/src/test/java/org/terasology/config/flexible/ui/SettingWidgetFactoryTest.java +++ b/engine-tests/src/test/java/org/terasology/config/flexible/ui/SettingWidgetFactoryTest.java @@ -26,8 +26,8 @@ void testCreateWidgetFor() { .thenReturn(Lists.newArrayList(NumberRangeConstraintWidgetFactory.class)); AssetManager assetManager = new AssetManager(mock(AssetTypeManager.class)); - - SettingWidgetFactory settingWidgetFactory = new SettingWidgetFactory(environment, assetManager); + SettingWidgetFactory settingWidgetFactory = new SettingWidgetFactory(environment, assetManager, + null); Setting setting = mock(Setting.class); diff --git a/engine/src/main/java/org/terasology/engine/config/Config.java b/engine/src/main/java/org/terasology/engine/config/Config.java index 2c3064317b..a0f58880c6 100644 --- a/engine/src/main/java/org/terasology/engine/config/Config.java +++ b/engine/src/main/java/org/terasology/engine/config/Config.java @@ -1,18 +1,5 @@ -/* - * Copyright 2013 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.config; @@ -31,17 +18,17 @@ import org.terasology.engine.core.paths.PathManager; import org.terasology.engine.core.subsystem.Resolution; import org.terasology.engine.entitySystem.Component; +import org.terasology.engine.utilities.gson.CaseInsensitiveEnumTypeAdapterFactory; +import org.terasology.engine.utilities.gson.InputHandler; +import org.terasology.engine.utilities.gson.ResolutionHandler; +import org.terasology.engine.utilities.gson.SetMultimapTypeAdapter; +import org.terasology.engine.utilities.gson.UriTypeAdapterFactory; import org.terasology.input.Input; import org.terasology.module.sandbox.API; import org.terasology.naming.Name; import org.terasology.naming.Version; import org.terasology.naming.gson.NameTypeAdapter; import org.terasology.naming.gson.VersionTypeAdapter; -import org.terasology.engine.utilities.gson.CaseInsensitiveEnumTypeAdapterFactory; -import org.terasology.engine.utilities.gson.InputHandler; -import org.terasology.engine.utilities.gson.ResolutionHandler; -import org.terasology.engine.utilities.gson.SetMultimapTypeAdapter; -import org.terasology.engine.utilities.gson.UriTypeAdapterFactory; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -93,10 +80,6 @@ public NetworkConfig getNetwork() { return config.getNetwork(); } - public PlayerConfig getPlayer() { - return config.getPlayer(); - } - public RenderingConfig getRendering() { return config.getRendering(); } diff --git a/engine/src/main/java/org/terasology/engine/config/PlayerConfig.java b/engine/src/main/java/org/terasology/engine/config/PlayerConfig.java index 494b4ca91f..c78eb061c7 100644 --- a/engine/src/main/java/org/terasology/engine/config/PlayerConfig.java +++ b/engine/src/main/java/org/terasology/engine/config/PlayerConfig.java @@ -1,108 +1,62 @@ -/* - * Copyright 2017 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.config; -import org.terasology.nui.Color; +import org.terasology.engine.config.flexible.AutoConfig; +import org.terasology.engine.config.flexible.Setting; +import org.terasology.engine.config.flexible.constraints.ColorConstraint; +import org.terasology.engine.config.flexible.constraints.NumberRangeConstraint; +import org.terasology.engine.config.flexible.constraints.StringConstraint; import org.terasology.engine.rendering.nui.layers.mainMenu.settings.CieCamColors; import org.terasology.engine.utilities.random.FastRandom; import org.terasology.engine.utilities.random.Random; -import org.terasology.engine.utilities.subscribables.AbstractSubscribable; +import org.terasology.nui.Color; import java.util.List; -public class PlayerConfig extends AbstractSubscribable { +import static org.terasology.engine.config.flexible.SettingArgument.constraint; +import static org.terasology.engine.config.flexible.SettingArgument.defaultValue; +import static org.terasology.engine.config.flexible.SettingArgument.name; +import static org.terasology.engine.config.flexible.SettingArgument.type; - public static final String DISCORD_PRESENCE = "DISCORD_PRESENCE"; - public static final String PLAYER_NAME = "PLAYER_NAME"; +public class PlayerConfig extends AutoConfig { private static final float DEFAULT_PLAYER_HEIGHT = 1.8f; private static final float DEFAULT_PLAYER_EYE_HEIGHT = 0.85f; - private static final boolean DEFAULT_DISCORD_PRESENCE = true; - - private String name = defaultPlayerName(); - - private Color color = defaultPlayerColor(); - - private Float height = DEFAULT_PLAYER_HEIGHT; - - private Float eyeHeight = DEFAULT_PLAYER_EYE_HEIGHT; - - private boolean hasEnteredUsername; - - private boolean discordPresence = DEFAULT_DISCORD_PRESENCE; - - public String getName() { - return name; - } - - public void setName(String name) { - String oldName = this.name; - this.name = name; - propertyChangeSupport.firePropertyChange(PLAYER_NAME, oldName, name); - } - - public Color getColor() { - return color; - } - - public void setColor(Color color) { - this.color = color; - } - - public Float getHeight() { - return height; - } - - public void setHeight(Float height) { - this.height = height; - } - - public Float getEyeHeight() { - return eyeHeight; - } - - public void setEyeHeight(Float eyeHeight) { - if (eyeHeight < this.height) { - this.eyeHeight = eyeHeight; - } - } - - public boolean hasEnteredUsername() { - return hasEnteredUsername; - } - - public void setHasEnteredUsername(boolean entered) { - this.hasEnteredUsername = entered; - } - - public void setDiscordPresence(boolean discordPresence) { - boolean oldValue = this.discordPresence; - this.discordPresence = discordPresence; - propertyChangeSupport.firePropertyChange(DISCORD_PRESENCE, oldValue, discordPresence); - } - - public boolean isDiscordPresence() { - return discordPresence; - } + public final Setting playerName = setting( + type(String.class), + defaultValue(defaultPlayerName()), + name("${engine:menu#player-name}"), + constraint(new StringConstraint( + StringConstraint.notEmptyOrNull(), + StringConstraint.maxLength(100)) + ) + ); + public final Setting color = setting( + type(Color.class), + defaultValue(defaultPlayerColor()), + name("${engine:menu#player-color}"), + constraint(new ColorConstraint()) + ); + public final Setting height = setting( + type(Float.class), + defaultValue(DEFAULT_PLAYER_HEIGHT), + name("${engine:menu#player-height}"), + constraint(new NumberRangeConstraint<>(1.5f, 2.0f, true, true)) + ); + public final Setting eyeHeight = setting( + type(Float.class), + defaultValue(DEFAULT_PLAYER_EYE_HEIGHT), + name("${engine:menu#player-eye-height}"), + constraint(new NumberRangeConstraint<>(0.5f, 1.5f, true, true)) + ); /** - * Generates the player's default name. The default name is the string "Player" followed by a random 5 digit code ranging from 10000 to 99999. + * Generates the player's default name. The default name is the string "Player" followed by a random 5 digit code + * ranging from 10000 to 99999. * * @return a String with the player's default name. */ @@ -120,4 +74,9 @@ private Color defaultPlayerColor() { List colors = CieCamColors.L65C65; return colors.get(rng.nextInt(colors.size())); } + + @Override + public String getName() { + return "${engine:menu#player-settings-title}"; + } } diff --git a/engine/src/main/java/org/terasology/engine/config/RootConfig.java b/engine/src/main/java/org/terasology/engine/config/RootConfig.java index e02dd75ace..4a260c7df5 100644 --- a/engine/src/main/java/org/terasology/engine/config/RootConfig.java +++ b/engine/src/main/java/org/terasology/engine/config/RootConfig.java @@ -1,18 +1,5 @@ -/* - * Copyright 2015 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.config; @@ -27,7 +14,6 @@ * and loaded in a JSON format. */ public final class RootConfig { - private PlayerConfig player = new PlayerConfig(); private PermissionConfig permission = new PermissionConfig(); private InputConfig input = new InputConfig(); private BindsConfig binds = new BindsConfig(); @@ -70,10 +56,6 @@ public NetworkConfig getNetwork() { return network; } - public PlayerConfig getPlayer() { - return player; - } - public RenderingConfig getRendering() { return rendering; } diff --git a/engine/src/main/java/org/terasology/engine/config/SystemConfig.java b/engine/src/main/java/org/terasology/engine/config/SystemConfig.java index ab4d33146e..b990e7d7b7 100644 --- a/engine/src/main/java/org/terasology/engine/config/SystemConfig.java +++ b/engine/src/main/java/org/terasology/engine/config/SystemConfig.java @@ -5,6 +5,7 @@ import org.terasology.engine.config.flexible.AutoConfig; import org.terasology.engine.config.flexible.Setting; +import org.terasology.engine.config.flexible.constraints.LocaleConstraint; import org.terasology.engine.config.flexible.constraints.NumberRangeConstraint; import java.util.Locale; @@ -13,6 +14,7 @@ import static org.terasology.engine.config.flexible.SettingArgument.constraint; import static org.terasology.engine.config.flexible.SettingArgument.defaultValue; +import static org.terasology.engine.config.flexible.SettingArgument.name; import static org.terasology.engine.config.flexible.SettingArgument.override; import static org.terasology.engine.config.flexible.SettingArgument.type; @@ -23,40 +25,47 @@ public class SystemConfig extends AutoConfig { public final Setting dayNightLengthInMs = setting( type(Long.class), defaultValue(1800000L), + name("Day/Night length (ms) (not yet)"), constraint(new NumberRangeConstraint<>(0L, Long.MAX_VALUE, false, false)) ); public final Setting maxThreads = setting( type(Integer.class), defaultValue(Runtime.getRuntime().availableProcessors() - 1), + name("Max threads(not yet)"), constraint(new NumberRangeConstraint<>(0, Integer.MAX_VALUE, false, false)) ); public final Setting maxSecondsBetweenSaves = setting( type(Integer.class), defaultValue(60), - constraint(new NumberRangeConstraint<>(0, Integer.MAX_VALUE, false, false)) + name("Seconds between saves"), + constraint(new NumberRangeConstraint<>(0, 1200, false, false)) ); public final Setting maxUnloadedChunksPercentageTillSave = setting( type(Integer.class), defaultValue(40), + name("Max unloaded chunks percentage till save"), constraint(new NumberRangeConstraint<>(0, 100, false, false)) ); public final Setting debugEnabled = setting( type(Boolean.class), - defaultValue(false) + defaultValue(false), + name("Debug mode") ); public final Setting monitoringEnabled = setting( type(Boolean.class), - defaultValue(false) + defaultValue(false), + name("Monitoring") ); public final Setting writeSaveGamesEnabled = setting( type(Boolean.class), defaultValue(true), + name("Game saves"), override(() -> Optional.ofNullable( System.getProperty(SAVED_GAMES_ENABLED_PROPERTY)) .map(Boolean::parseBoolean)) @@ -65,12 +74,15 @@ public class SystemConfig extends AutoConfig { public final Setting chunkGenerationFailTimeoutInMs = setting( type(Long.class), defaultValue(1800000L), - constraint(new NumberRangeConstraint<>(0L, Long.MAX_VALUE, false, false)) + name("Chunk generation fail timeout (ms)"), + constraint(new NumberRangeConstraint<>(0L, 3600000L, false, false)) ); public final Setting locale = setting( type(Locale.class), - defaultValue(Locale.getDefault(Category.DISPLAY)) + defaultValue(Locale.getDefault(Category.DISPLAY)), + name("${engine:menu#settings-language}"), + constraint(new LocaleConstraint(Locale.getAvailableLocales())) // TODO provide translate project's locales (Pirate lang don't works) ); @Override diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/AutoConfigManager.java b/engine/src/main/java/org/terasology/engine/config/flexible/AutoConfigManager.java index 50195a2f11..3f64f67bc5 100644 --- a/engine/src/main/java/org/terasology/engine/config/flexible/AutoConfigManager.java +++ b/engine/src/main/java/org/terasology/engine/config/flexible/AutoConfigManager.java @@ -32,7 +32,7 @@ public class AutoConfigManager { private static final Logger logger = LoggerFactory.getLogger(AutoConfigManager.class); - private final Set loadedConfigs = Sets.newHashSet(); + private final Set loadedConfigs = Sets.newLinkedHashSet(); private final Serializer serializer; public AutoConfigManager(Serializer serializer) { @@ -109,7 +109,7 @@ private void saveConfigToDisk(AutoConfig config) { // TODO: Save when screen for config closed Path configPath = getConfigPath(config.getId()); try (OutputStream output = Files.newOutputStream(configPath, StandardOpenOption.CREATE)) { - serializer.serialize(config, TypeInfo.of(AutoConfig.class), output); + serializer.serialize(config, TypeInfo.of((Class)config.getClass()), output); } catch (IOException e) { logger.error("Error while saving config {} to disk", config.getId(), e); } diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/bindings/MappingBinding.java b/engine/src/main/java/org/terasology/engine/config/flexible/bindings/MappingBinding.java new file mode 100644 index 0000000000..42e9057e79 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/bindings/MappingBinding.java @@ -0,0 +1,30 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.bindings; + +import org.terasology.nui.databinding.Binding; + +import java.util.function.Function; + +public class MappingBinding implements Binding { + private final Binding internalBinging; + private final Function setMapping; + private final Function getMapping; + + public MappingBinding(Binding internalBinging, Function setMapping, Function getMapping) { + this.internalBinging = internalBinging; + this.setMapping = setMapping; + this.getMapping = getMapping; + } + + @Override + public T get() { + return getMapping.apply(internalBinging.get()); + } + + @Override + public void set(T value) { + internalBinging.set(setMapping.apply(value)); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/bindings/SettingBinding.java b/engine/src/main/java/org/terasology/engine/config/flexible/bindings/SettingBinding.java new file mode 100644 index 0000000000..0bd07291e2 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/bindings/SettingBinding.java @@ -0,0 +1,27 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.bindings; + + +import org.terasology.engine.config.flexible.Setting; +import org.terasology.nui.databinding.Binding; + +public class SettingBinding implements Binding { + private final Setting setting; + + public SettingBinding(Setting setting) { + this.setting = setting; + } + + + @Override + public T get() { + return setting.get(); + } + + @Override + public void set(T value) { + setting.set(value); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/constraints/ColorConstraint.java b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/ColorConstraint.java new file mode 100644 index 0000000000..d8e166a547 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/ColorConstraint.java @@ -0,0 +1,18 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.constraints; + +import org.terasology.nui.Color; + +public class ColorConstraint implements SettingConstraint { + + @Override + public boolean isSatisfiedBy(Color value) { + return true; + } + + @Override + public void warnUnsatisfiedBy(Color value) { + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/constraints/LocaleConstraint.java b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/LocaleConstraint.java new file mode 100644 index 0000000000..e5b7c7273c --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/LocaleConstraint.java @@ -0,0 +1,42 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.constraints; + +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +public class LocaleConstraint implements SettingConstraint { + + private static final Logger logger = LoggerFactory.getLogger(LocaleConstraint.class); + + private final Set locales; + + public LocaleConstraint(Set locales) { + this.locales = locales; + } + + public LocaleConstraint(Locale... locales) { + this.locales = Sets.newHashSet(locales); + } + + @Override + public boolean isSatisfiedBy(Locale value) { + return locales.contains(value); + } + + @Override + public void warnUnsatisfiedBy(Locale value) { + logger.warn("Locale {} should be one of {}", + value, + locales.stream() + .map(Locale::getLanguage) + .collect(Collectors.joining(",", "[", "]")) + ); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/constraints/PredicateWithDescription.java b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/PredicateWithDescription.java new file mode 100644 index 0000000000..c002cc51d1 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/PredicateWithDescription.java @@ -0,0 +1,25 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.constraints; + +import java.util.function.Predicate; + +public class PredicateWithDescription implements Predicate { + private final Predicate predicate; + private final String description; + + public PredicateWithDescription(String description, Predicate predicate) { + this.predicate = predicate; + this.description = description; + } + + public String getDescription() { + return description; + } + + @Override + public boolean test(T s) { + return predicate.test(s); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/constraints/StringConstraint.java b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/StringConstraint.java new file mode 100644 index 0000000000..dca205531b --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/constraints/StringConstraint.java @@ -0,0 +1,61 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.constraints; + +import com.google.common.base.Predicates; +import com.google.common.base.Strings; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class StringConstraint implements SettingConstraint { + private static final Logger logger = LoggerFactory.getLogger(StringConstraint.class); + private final List> predicates; + + public StringConstraint(Predicate... predicates) { + this.predicates = Arrays.asList(predicates); + } + + public static Predicate notEmptyOrNull() { + return new PredicateWithDescription<>("not null or not empty", Predicates.not(Strings::isNullOrEmpty)); + } + + public static Predicate maxLength(int length) { + return new PredicateWithDescription<>("length should be less than " + length, s -> s.length() < length); + } + + public static Predicate regex(String regex) { + Pattern pattern = Pattern.compile(regex); + return new PredicateWithDescription<>("matches regex: \"" + regex + "\"", s -> pattern.matcher(s).matches()); + } + + @NotNull + private static String getDescription(Predicate p) { + if (p instanceof PredicateWithDescription) { + return ((PredicateWithDescription) p).getDescription(); + } else { + return "Predicate without description"; + } + } + + @Override + public boolean isSatisfiedBy(String value) { + return predicates.stream().allMatch(p -> p.test(value)); + } + + @Override + public void warnUnsatisfiedBy(String value) { + logger.warn("String [{}] does not match the conditions: {}", value, + predicates.stream() + .filter(p -> !p.test(value)) + .map(StringConstraint::getDescription) + .collect(Collectors.joining(",", "[", "]"))); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigScreen.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigScreen.java index 1d7be5ce8a..21ae81b593 100644 --- a/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigScreen.java +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigScreen.java @@ -9,14 +9,14 @@ import org.terasology.engine.config.flexible.AutoConfig; import org.terasology.engine.config.flexible.AutoConfigManager; import org.terasology.engine.core.module.ModuleManager; +import org.terasology.engine.registry.In; +import org.terasology.engine.rendering.nui.CoreScreenLayer; import org.terasology.nui.UIWidget; import org.terasology.nui.WidgetUtil; import org.terasology.nui.databinding.Binding; import org.terasology.nui.databinding.DefaultBinding; import org.terasology.nui.layouts.ColumnLayout; import org.terasology.nui.widgets.types.TypeWidgetLibrary; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.CoreScreenLayer; import java.util.Optional; @@ -46,7 +46,7 @@ public void initialise() { if (widget.isPresent()) { mainContainer.addWidget(widget.get()); } else { - logger.warn("Cannot create widget for config:{}", config.getId()); + logger.warn("Cannot create widget for config: {}", config.getId()); } } WidgetUtil.trySubscribe(this, "close", button -> triggerBackAnimation()); diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigWidgetFactory.java index 77ed14fc53..57bf68e748 100644 --- a/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigWidgetFactory.java +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigWidgetFactory.java @@ -7,8 +7,10 @@ import org.terasology.assets.management.AssetManager; import org.terasology.engine.config.flexible.AutoConfig; import org.terasology.engine.config.flexible.Setting; +import org.terasology.engine.context.Context; import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.i18n.TranslationSystem; +import org.terasology.engine.registry.In; import org.terasology.nui.UIWidget; import org.terasology.nui.layouts.PropertyLayout; import org.terasology.nui.properties.Property; @@ -17,7 +19,6 @@ import org.terasology.nui.widgets.types.TypeWidgetFactory; import org.terasology.nui.widgets.types.TypeWidgetLibrary; import org.terasology.reflection.TypeInfo; -import org.terasology.engine.registry.In; import java.util.ArrayList; import java.util.Collection; @@ -54,8 +55,11 @@ public class AutoConfigWidgetFactory implements TypeWidgetFactory { @In private TranslationSystem translationSystem; - public AutoConfigWidgetFactory(ModuleManager moduleManager, AssetManager assetManager) { - this.settingWidgetFactory = new SettingWidgetFactory(moduleManager.getEnvironment(), assetManager); + public AutoConfigWidgetFactory(ModuleManager moduleManager, + AssetManager assetManager, + Context context) { + this.settingWidgetFactory = + new SettingWidgetFactory(moduleManager.getEnvironment(), assetManager, context); this.assetManager = assetManager; } @@ -75,7 +79,7 @@ public UIWidget buildWidgetFor(AutoConfig config) { Optional settingWidget = settingWidgetFactory.createWidgetFor(setting); if (!settingWidget.isPresent()) { - logger.error("Couldn't find a widget for the Setting"); + logger.error("Couldn't find a widget for the Setting [{}]", setting.getHumanReadableName()); continue; } diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/ColorConstraintWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/ColorConstraintWidgetFactory.java new file mode 100644 index 0000000000..9a3a9eef05 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/ColorConstraintWidgetFactory.java @@ -0,0 +1,87 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.ui; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.math.DoubleMath; +import org.terasology.assets.ResourceUrn; +import org.terasology.assets.management.AssetManager; +import org.terasology.engine.config.flexible.bindings.MappingBinding; +import org.terasology.engine.config.flexible.bindings.SettingBinding; +import org.terasology.engine.config.flexible.constraints.ColorConstraint; +import org.terasology.engine.registry.In; +import org.terasology.engine.rendering.assets.texture.Texture; +import org.terasology.engine.rendering.assets.texture.TextureUtil; +import org.terasology.engine.rendering.nui.layers.mainMenu.settings.CieCamColors; +import org.terasology.nui.Color; +import org.terasology.nui.UIWidget; +import org.terasology.nui.widgets.UIImage; +import org.terasology.nui.widgets.UISlider; + +import java.math.RoundingMode; +import java.util.List; + +public class ColorConstraintWidgetFactory extends AssetBackedConstraintWidgetFactory { + + @In + private AssetManager assetManager; + + private final List colors = CieCamColors.L65C65; + + public ColorConstraintWidgetFactory() { + super("engine:colorPickerWidget"); + } + + @Override + protected void bindWidgetToSetting(UIWidget widget) { + UIImage img = widget.find("image", UIImage.class); + if (img != null) { + ResourceUrn uri = TextureUtil.getTextureUriForColor(Color.WHITE); + Texture tex = assetManager.getAsset(uri, Texture.class).get(); + img.setImage(tex); + img.bindTint(new SettingBinding<>(getSetting())); + } + + UISlider slider = widget.find("tone", UISlider.class); + slider.setIncrement(0.01f); + Function constant = Functions.constant(" "); // ensure a certain width + slider.setLabelFunction(constant); + + slider.bindValue( + new MappingBinding<>( + new SettingBinding<>(getSetting()), + this::findClosestColor, + this::findClosestIndex + )); + } + + private float findClosestIndex(Color color) { + int best = 0; + float minDist = Float.MAX_VALUE; + for (int i = 0; i < colors.size(); i++) { + Color other = colors.get(i); + float dr = other.rf() - color.rf(); + float dg = other.gf() - color.gf(); + float db = other.bf() - color.bf(); + + // there are certainly smarter ways to measure color distance, + // but Euclidean distance is good enough for the purpose + float dist = dr * dr + dg * dg + db * db; + if (dist < minDist) { + minDist = dist; + best = i; + } + } + + float max = colors.size() - 1; + return best / max; + } + + private Color findClosestColor(float findex) { + int index = DoubleMath.roundToInt(findex * (double) (colors.size() - 1), RoundingMode.HALF_UP); + Color color = colors.get(index); + return color; + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/DefaultConstraintWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/DefaultConstraintWidgetFactory.java new file mode 100644 index 0000000000..886bd59868 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/DefaultConstraintWidgetFactory.java @@ -0,0 +1,38 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.ui; + +import org.terasology.engine.config.flexible.Setting; +import org.terasology.engine.config.flexible.constraints.SettingConstraint; +import org.terasology.engine.context.Context; +import org.terasology.nui.UIWidget; +import org.terasology.nui.databinding.Binding; +import org.terasology.nui.widgets.types.TypeWidgetLibrary; + +import java.util.Optional; + +public class DefaultConstraintWidgetFactory extends ConstraintWidgetFactory> { + private final Context context; + + public DefaultConstraintWidgetFactory(Context context) { + this.context = context; + } + + @Override + protected Optional buildWidget() { + Setting setting = getSetting(); + Binding binding = new Binding() { + @Override + public T get() { + return setting.get(); + } + + @Override + public void set(T value) { + setting.set(value); + } + }; + return context.get(TypeWidgetLibrary.class).getWidget(binding, setting.getValueType()); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java new file mode 100644 index 0000000000..d66eac75c5 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/LocaleConstraintWidgetFactory.java @@ -0,0 +1,76 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.config.flexible.ui; + +import com.google.common.collect.Lists; +import org.terasology.engine.config.flexible.Setting; +import org.terasology.engine.config.flexible.constraints.LocaleConstraint; +import org.terasology.engine.core.SimpleUri; +import org.terasology.engine.i18n.TranslationProject; +import org.terasology.engine.i18n.TranslationSystem; +import org.terasology.engine.rendering.nui.layers.mainMenu.settings.LocaleRenderer; +import org.terasology.nui.UIWidget; +import org.terasology.nui.databinding.Binding; +import org.terasology.nui.widgets.UIDropdownScrollable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.function.Function; + +public class LocaleConstraintWidgetFactory extends ConstraintWidgetFactory { + + /** + * Remove language x from this languagesExcluded table when it is ready for testing + */ + private final Locale[] languagesExcluded = + {Locale.forLanguageTag("zh"), // TODO: Chinese symbols not yet available + Locale.forLanguageTag("hi"), // TODO: Hindi (Indian) symbols not yet available + Locale.forLanguageTag("ar"), // TODO: Arabic symbols not yet available, no translated entries yet + Locale.forLanguageTag("ko"), // TODO: Korean symbols not yet available + Locale.forLanguageTag("fa")}; // TODO: Farsi (Persian) symbols not yet available + + + private final TranslationSystem translationSystem; + + public LocaleConstraintWidgetFactory(TranslationSystem translationSystem) { + this.translationSystem = translationSystem; + } + + @Override + protected Optional buildWidget() { + Setting setting = getSetting(); + + Binding binding = new Binding() { + @Override + public Locale get() { + return setting.get(); + } + + @Override + public void set(Locale value) { + setting.set(value); + } + }; + + UIDropdownScrollable dropdownScrollable = new UIDropdownScrollable<>(); + SimpleUri menuUri = new SimpleUri("engine:menu"); + TranslationProject menuProject = translationSystem.getProject(menuUri); + List locales = new ArrayList<>(menuProject.getAvailableLocales()); + for (Locale languageExcluded : languagesExcluded) { + locales.remove(languageExcluded); + } + Collections.sort(locales, (Comparator.comparing((Function) Object::toString))); + dropdownScrollable.setOptions(Lists.newArrayList(locales)); + dropdownScrollable.setVisibleOptions(5); // Set maximum number of options visible for scrolling + dropdownScrollable.bindSelection(binding); + + dropdownScrollable.setOptionRenderer(new LocaleRenderer(translationSystem)); + + return Optional.of(dropdownScrollable); + } +} diff --git a/engine/src/main/java/org/terasology/engine/config/flexible/ui/SettingWidgetFactory.java b/engine/src/main/java/org/terasology/engine/config/flexible/ui/SettingWidgetFactory.java index 6cff37d2fd..a1e437ef8e 100644 --- a/engine/src/main/java/org/terasology/engine/config/flexible/ui/SettingWidgetFactory.java +++ b/engine/src/main/java/org/terasology/engine/config/flexible/ui/SettingWidgetFactory.java @@ -1,4 +1,4 @@ -// Copyright 2020 The Terasology Foundation +// Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.config.flexible.ui; @@ -6,11 +6,12 @@ import org.terasology.assets.management.AssetManager; import org.terasology.engine.config.flexible.Setting; import org.terasology.engine.config.flexible.constraints.SettingConstraint; -import org.terasology.module.ModuleEnvironment; -import org.terasology.nui.UIWidget; +import org.terasology.engine.context.Context; import org.terasology.engine.registry.In; import org.terasology.engine.registry.InjectionHelper; import org.terasology.engine.utilities.ReflectionUtil; +import org.terasology.module.ModuleEnvironment; +import org.terasology.nui.UIWidget; import java.lang.reflect.Type; import java.util.Optional; @@ -29,10 +30,12 @@ public class SettingWidgetFactory { private final ModuleEnvironment environment; private final AssetManager assetManager; + private final Context context; - public SettingWidgetFactory(ModuleEnvironment environment, AssetManager assetManager) { + public SettingWidgetFactory(ModuleEnvironment environment, AssetManager assetManager, Context context) { this.environment = environment; this.assetManager = assetManager; + this.context = context; } /** @@ -43,7 +46,8 @@ public SettingWidgetFactory(ModuleEnvironment environment, AssetManager assetMan */ public Optional createWidgetFor(Setting setting) { return getConstraintWidgetFactory(setting) - .flatMap(factory -> factory.buildWidgetFor(setting)); + .orElseGet(()-> new DefaultConstraintWidgetFactory<>(context)) + .buildWidgetFor(setting); } /** @@ -55,22 +59,23 @@ public Optional createWidgetFor(Setting setting) { */ Optional> getConstraintWidgetFactory(Setting setting) { SettingConstraint constraint = setting.getConstraint(); + if (constraint != null) { + for (Class widgetType + : environment.getSubtypesOf(ConstraintWidgetFactory.class)) { + Type constraintType = + ReflectionUtil.getTypeParameterForSuper(widgetType, ConstraintWidgetFactory.class, 1); - for (Class widgetType : environment.getSubtypesOf(ConstraintWidgetFactory.class)) { - Type constraintType = - ReflectionUtil.getTypeParameterForSuper(widgetType, ConstraintWidgetFactory.class, 1); + if (constraint.getClass().equals(ReflectionUtil.getRawType(constraintType))) { - if (constraint.getClass().equals(ReflectionUtil.getRawType(constraintType))) { - try { - ConstraintWidgetFactory factory = widgetType.newInstance(); + ConstraintWidgetFactory factory = + InjectionHelper.createWithConstructorInjection(widgetType, context); InjectionHelper.inject(factory, In.class, ImmutableMap.of(AssetManager.class, assetManager)); return Optional.of(factory); - } catch (InstantiationException | IllegalAccessException ignored) { } + + } } } - return Optional.empty(); - } } diff --git a/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/SetupLocalPlayer.java b/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/SetupLocalPlayer.java index 2ee896460c..01dc362f4e 100644 --- a/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/SetupLocalPlayer.java +++ b/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/SetupLocalPlayer.java @@ -1,22 +1,8 @@ -/* - * Copyright 2013 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.core.modes.loadProcesses; -import org.terasology.engine.config.Config; import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.context.Context; import org.terasology.engine.core.modes.SingleStepLoadProcess; @@ -41,8 +27,9 @@ public String getMessage() { @Override public boolean step() { - PlayerConfig playerConfig = context.get(Config.class).getPlayer(); - Client localClient = context.get(NetworkSystem.class).joinLocal(playerConfig.getName(), playerConfig.getColor()); + PlayerConfig playerConfig = context.get(PlayerConfig.class); + Client localClient = context.get(NetworkSystem.class).joinLocal(playerConfig.playerName.get(), + playerConfig.color.get()); context.get(LocalPlayer.class).setClientEntity(localClient.getEntity()); return true; } diff --git a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java index b2cc506642..1a8589db68 100644 --- a/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java +++ b/engine/src/main/java/org/terasology/engine/i18n/TranslationSystemImpl.java @@ -25,8 +25,7 @@ import java.util.function.Consumer; /** - * A translation system that uses {@link Translation} data assets to - * perform the lookup. + * A translation system that uses {@link Translation} data assets to perform the lookup. */ public class TranslationSystemImpl implements TranslationSystem { @@ -46,7 +45,6 @@ public TranslationSystemImpl(Context context) { systemConfig = context.get(SystemConfig.class); assetManager = context.get(AssetManager.class); - refresh(); } diff --git a/engine/src/main/java/org/terasology/engine/logic/characters/GazeAuthoritySystem.java b/engine/src/main/java/org/terasology/engine/logic/characters/GazeAuthoritySystem.java index 88e77a6fc3..b444543aa8 100644 --- a/engine/src/main/java/org/terasology/engine/logic/characters/GazeAuthoritySystem.java +++ b/engine/src/main/java/org/terasology/engine/logic/characters/GazeAuthoritySystem.java @@ -5,7 +5,7 @@ import org.joml.Quaternionf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.entitySystem.entity.EntityBuilder; import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; @@ -32,7 +32,7 @@ public class GazeAuthoritySystem extends BaseComponentSystem { @In EntityManager entityManager; @In - private Config config; + private PlayerConfig playerConfig; @ReceiveEvent public void ensureGazeContainerEntitiesCreated(OnActivatedComponent event, EntityRef entityRef, GazeMountPointComponent gazeMountPointComponent, @@ -41,7 +41,7 @@ public void ensureGazeContainerEntitiesCreated(OnActivatedComponent event, Entit gazeMountPointComponent.gazeEntity = createGazeEntity(); entityRef.saveComponent(gazeMountPointComponent); } - gazeMountPointComponent.translate.y = config.getPlayer().getEyeHeight(); + gazeMountPointComponent.translate.y = playerConfig.eyeHeight.get(); Location.attachChild(entityRef, gazeMountPointComponent.gazeEntity, gazeMountPointComponent.translate, new Quaternionf()); } diff --git a/engine/src/main/java/org/terasology/engine/logic/players/LocalPlayerSystem.java b/engine/src/main/java/org/terasology/engine/logic/players/LocalPlayerSystem.java index 21a403ecd8..c7d2992862 100644 --- a/engine/src/main/java/org/terasology/engine/logic/players/LocalPlayerSystem.java +++ b/engine/src/main/java/org/terasology/engine/logic/players/LocalPlayerSystem.java @@ -8,6 +8,7 @@ import org.joml.Vector3f; import org.terasology.assets.ResourceUrn; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.core.SimpleUri; import org.terasology.engine.core.Time; import org.terasology.engine.core.subsystem.config.BindsManager; @@ -17,15 +18,6 @@ import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RenderSystem; import org.terasology.engine.entitySystem.systems.UpdateSubscriberSystem; -import org.terasology.engine.logic.characters.CharacterComponent; -import org.terasology.engine.logic.characters.CharacterMoveInputEvent; -import org.terasology.engine.logic.characters.CharacterMovementComponent; -import org.terasology.engine.logic.characters.MovementMode; -import org.terasology.engine.logic.location.LocationComponent; -import org.terasology.engine.logic.players.event.LocalPlayerInitializedEvent; -import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; -import org.terasology.input.ButtonState; -import org.terasology.input.Input; import org.terasology.engine.input.InputSystem; import org.terasology.engine.input.binds.interaction.FrobButton; import org.terasology.engine.input.binds.inventory.UseItemButton; @@ -45,12 +37,18 @@ import org.terasology.engine.input.binds.movement.VerticalRealMovementAxis; import org.terasology.engine.input.events.MouseAxisEvent; import org.terasology.engine.input.events.MouseAxisEvent.MouseAxis; -import org.terasology.joml.geom.AABBf; +import org.terasology.engine.logic.characters.CharacterComponent; import org.terasology.engine.logic.characters.CharacterHeldItemComponent; +import org.terasology.engine.logic.characters.CharacterMoveInputEvent; +import org.terasology.engine.logic.characters.CharacterMovementComponent; +import org.terasology.engine.logic.characters.MovementMode; import org.terasology.engine.logic.characters.events.OnItemUseEvent; import org.terasology.engine.logic.characters.events.ScaleToRequest; import org.terasology.engine.logic.characters.interactions.InteractionUtil; import org.terasology.engine.logic.delay.DelayManager; +import org.terasology.engine.logic.location.LocationComponent; +import org.terasology.engine.logic.players.event.LocalPlayerInitializedEvent; +import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.engine.network.ClientComponent; import org.terasology.engine.network.NetworkMode; import org.terasology.engine.network.NetworkSystem; @@ -65,6 +63,9 @@ import org.terasology.engine.world.block.Block; import org.terasology.engine.world.block.BlockComponent; import org.terasology.engine.world.block.regions.BlockRegionComponent; +import org.terasology.input.ButtonState; +import org.terasology.input.Input; +import org.terasology.joml.geom.AABBf; import java.util.List; @@ -87,6 +88,8 @@ public class LocalPlayerSystem extends BaseComponentSystem implements UpdateSubs @In private Config config; @In + private PlayerConfig playerConfig; + @In private InputSystem inputSystem; @In @@ -224,7 +227,7 @@ private void startAutoMove() { public void onPlayerSpawn(OnPlayerSpawnedEvent event, EntityRef character) { if (character.equals(localPlayer.getCharacterEntity())) { // update character height as given in player settings - ScaleToRequest scaleRequest = new ScaleToRequest(config.getPlayer().getHeight()); + ScaleToRequest scaleRequest = new ScaleToRequest(playerConfig.height.get()); localPlayer.getCharacterEntity().send(scaleRequest); // Trigger updating the player camera position as soon as the local player is spawned. diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java index c57e548e25..889a9e8223 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java @@ -1,4 +1,4 @@ -// Copyright 2020 The Terasology Foundation +// Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -9,17 +9,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.core.EngineTime; import org.terasology.engine.core.TerasologyConstants; import org.terasology.engine.core.Time; import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.core.paths.PathManager; +import org.terasology.engine.network.JoinStatus; +import org.terasology.engine.registry.CoreRegistry; import org.terasology.module.ModuleLoader; import org.terasology.naming.Name; import org.terasology.naming.Version; -import org.terasology.engine.network.JoinStatus; import org.terasology.protobuf.NetData; -import org.terasology.engine.registry.CoreRegistry; import java.io.BufferedOutputStream; import java.io.IOException; @@ -290,12 +291,13 @@ private void receivedServerInfo(ChannelHandlerContext channelHandlerContext, Net */ private void sendJoin(ChannelHandlerContext channelHandlerContext) { Config config = CoreRegistry.get(Config.class); + PlayerConfig playerConfig = CoreRegistry.get(PlayerConfig.class); NetData.JoinMessage.Builder bldr = NetData.JoinMessage.newBuilder(); NetData.Color.Builder clrbldr = NetData.Color.newBuilder(); - bldr.setName(config.getPlayer().getName()); + bldr.setName(playerConfig.playerName.get()); bldr.setViewDistanceLevel(config.getRendering().getViewDistance().getIndex()); - bldr.setColor(clrbldr.setRgba(config.getPlayer().getColor().rgba()).build()); + bldr.setColor(clrbldr.setRgba(playerConfig.color.get().rgba()).build()); channelHandlerContext.channel().writeAndFlush(NetData.NetMessage.newBuilder().setJoin(bldr).build()); } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/EnterUsernamePopup.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/EnterUsernamePopup.java index 986f85eb1f..d4832ef74e 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/EnterUsernamePopup.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/EnterUsernamePopup.java @@ -4,33 +4,30 @@ import com.google.common.base.Strings; import org.terasology.assets.ResourceUrn; -import org.terasology.engine.config.Config; import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.i18n.TranslationSystem; +import org.terasology.engine.registry.In; +import org.terasology.engine.rendering.nui.CoreScreenLayer; import org.terasology.nui.WidgetUtil; import org.terasology.nui.databinding.ReadOnlyBinding; import org.terasology.nui.widgets.UIButton; import org.terasology.nui.widgets.UIText; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.CoreScreenLayer; public class EnterUsernamePopup extends CoreScreenLayer { public static final ResourceUrn ASSET_URI = new ResourceUrn("engine:enterUsernamePopup"); - @In - private Config config; @In private TranslationSystem translationSystem; + @In + private PlayerConfig playerConfig; private UIText username; - private PlayerConfig playerConfig; @Override public void initialise() { - playerConfig = config.getPlayer(); username = find("username", UIText.class); - username.setText(playerConfig.getName()); + username.setText(playerConfig.playerName.get()); username.bindTooltipString(new ReadOnlyBinding() { @Override public String get() { @@ -41,8 +38,7 @@ public String get() { UIButton okButton = find("ok", UIButton.class); if (okButton != null) { okButton.subscribe(button -> { - playerConfig.setName(username.getText().trim()); - playerConfig.setHasEnteredUsername(true); + playerConfig.playerName.set(username.getText().trim()); getManager().popScreen(); }); okButton.bindEnabled(new ReadOnlyBinding() { @@ -61,7 +57,6 @@ public String get() { } WidgetUtil.trySubscribe(this, "cancel", button -> { - playerConfig.setHasEnteredUsername(true); getManager().popScreen(); }); } @@ -70,7 +65,7 @@ public String get() { public void onOpened() { super.onOpened(); if (username != null) { - username.setText(config.getPlayer().getName()); + username.setText(playerConfig.playerName.get()); } } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java index 2767e46549..01a768f1a9 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import org.terasology.assets.ResourceUrn; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.config.ServerInfo; import org.terasology.engine.core.GameEngine; import org.terasology.engine.core.GameThread; @@ -16,15 +17,19 @@ import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.i18n.TranslationSystem; import org.terasology.engine.identity.storageServiceClient.StorageServiceWorker; -import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems; -import org.terasology.input.Keyboard; -import org.terasology.module.ModuleRegistry; -import org.terasology.naming.NameVersion; import org.terasology.engine.network.JoinStatus; import org.terasology.engine.network.NetworkSystem; import org.terasology.engine.network.PingService; import org.terasology.engine.network.ServerInfoMessage; import org.terasology.engine.network.ServerInfoService; +import org.terasology.engine.registry.In; +import org.terasology.engine.rendering.nui.CoreScreenLayer; +import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems; +import org.terasology.engine.world.internal.WorldInfo; +import org.terasology.engine.world.time.WorldTime; +import org.terasology.input.Keyboard; +import org.terasology.module.ModuleRegistry; +import org.terasology.naming.NameVersion; import org.terasology.nui.Color; import org.terasology.nui.FontColor; import org.terasology.nui.WidgetUtil; @@ -38,10 +43,6 @@ import org.terasology.nui.widgets.UIButton; import org.terasology.nui.widgets.UILabel; import org.terasology.nui.widgets.UIList; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.CoreScreenLayer; -import org.terasology.engine.world.internal.WorldInfo; -import org.terasology.engine.world.time.WorldTime; import java.io.IOException; import java.util.ArrayList; @@ -62,6 +63,8 @@ public class JoinGameScreen extends CoreScreenLayer { @In private Config config; + @In + private PlayerConfig playerConfig; @In private NetworkSystem networkSystem; @@ -148,7 +151,7 @@ public void onOpened() { infoService = new ServerInfoService(); - if (!config.getPlayer().hasEnteredUsername()) { + if (playerConfig.playerName.getDefaultValue().equals(playerConfig.playerName.get())) { getManager().pushScreen(EnterUsernamePopup.ASSET_URI, EnterUsernamePopup.class); } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectGameScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectGameScreen.java index e137762e6d..84d8d70ac3 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectGameScreen.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectGameScreen.java @@ -9,15 +9,15 @@ import org.terasology.engine.core.modes.StateLoading; import org.terasology.engine.core.paths.PathManager; import org.terasology.engine.game.GameManifest; +import org.terasology.engine.network.NetworkMode; +import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems; import org.terasology.engine.rendering.nui.layers.mainMenu.gameDetailsScreen.GameDetailsScreen; import org.terasology.engine.rendering.nui.layers.mainMenu.savedGames.GameInfo; import org.terasology.engine.rendering.nui.layers.mainMenu.savedGames.GameProvider; -import org.terasology.engine.network.NetworkMode; import org.terasology.nui.databinding.ReadOnlyBinding; import org.terasology.nui.widgets.UIButton; import org.terasology.nui.widgets.UILabel; -import org.terasology.engine.registry.CoreRegistry; import java.io.IOException; import java.nio.file.Files; @@ -124,7 +124,7 @@ public void onOpened() { triggerForwardAnimation(newGameScreen); } - if (isLoadingAsServer() && !super.config.getPlayer().hasEnteredUsername()) { + if (isLoadingAsServer() && super.playerConfig.playerName.getDefaultValue().equals(super.playerConfig.playerName.get())) { getManager().pushScreen(EnterUsernamePopup.ASSET_URI, EnterUsernamePopup.class); } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectionScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectionScreen.java index 0b690aa1e3..ea8cb66dca 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectionScreen.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectionScreen.java @@ -6,25 +6,26 @@ import org.slf4j.LoggerFactory; import org.terasology.assets.ResourceUrn; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.core.TerasologyConstants; import org.terasology.engine.i18n.TranslationSystem; +import org.terasology.engine.persistence.internal.GamePreviewImageProvider; +import org.terasology.engine.registry.In; import org.terasology.engine.rendering.assets.texture.AWTTextureFormat; 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.layers.mainMenu.savedGames.GameInfo; +import org.terasology.engine.utilities.Assets; +import org.terasology.engine.utilities.FilesUtil; +import org.terasology.engine.world.generator.internal.WorldGeneratorInfo; +import org.terasology.engine.world.generator.internal.WorldGeneratorManager; import org.terasology.naming.Name; import org.terasology.naming.NameVersion; import org.terasology.nui.widgets.UIImage; import org.terasology.nui.widgets.UIImageSlideshow; import org.terasology.nui.widgets.UILabel; import org.terasology.nui.widgets.UIList; -import org.terasology.engine.persistence.internal.GamePreviewImageProvider; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.CoreScreenLayer; -import org.terasology.engine.utilities.Assets; -import org.terasology.engine.utilities.FilesUtil; -import org.terasology.engine.world.generator.internal.WorldGeneratorInfo; -import org.terasology.engine.world.generator.internal.WorldGeneratorManager; import java.awt.image.BufferedImage; import java.io.IOException; @@ -48,6 +49,9 @@ public abstract class SelectionScreen extends CoreScreenLayer { @In protected Config config; + @In + protected PlayerConfig playerConfig; + @In protected WorldGeneratorManager worldGeneratorManager; diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/settings/PlayerSettingsScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/settings/PlayerSettingsScreen.java index 4421a499bb..efe3ddd9a7 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/settings/PlayerSettingsScreen.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/settings/PlayerSettingsScreen.java @@ -2,45 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.rendering.nui.layers.mainMenu.settings; -import com.google.common.base.Function; -import com.google.common.base.Functions; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import com.google.common.math.DoubleMath; import org.terasology.assets.ResourceUrn; -import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.config.SystemConfig; import org.terasology.engine.context.Context; -import org.terasology.engine.core.SimpleUri; -import org.terasology.engine.i18n.TranslationProject; import org.terasology.engine.i18n.TranslationSystem; import org.terasology.engine.identity.storageServiceClient.StorageServiceWorker; import org.terasology.engine.identity.storageServiceClient.StorageServiceWorkerStatus; -import org.terasology.engine.rendering.assets.texture.Texture; -import org.terasology.engine.rendering.assets.texture.TextureUtil; -import org.terasology.engine.rendering.nui.animation.MenuAnimationSystems; -import org.terasology.nui.Color; -import org.terasology.nui.WidgetUtil; -import org.terasology.nui.databinding.DefaultBinding; -import org.terasology.nui.databinding.ReadOnlyBinding; -import org.terasology.nui.widgets.UIButton; -import org.terasology.nui.widgets.UICheckbox; -import org.terasology.nui.widgets.UIDropdownScrollable; -import org.terasology.nui.widgets.UIImage; -import org.terasology.nui.widgets.UILabel; -import org.terasology.nui.widgets.UISlider; -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.animation.MenuAnimationSystems; import org.terasology.engine.rendering.nui.layers.mainMenu.StorageServiceLoginPopup; import org.terasology.engine.rendering.nui.layers.mainMenu.ThreeButtonPopup; -import org.terasology.engine.utilities.Assets; - -import java.math.RoundingMode; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; +import org.terasology.nui.WidgetUtil; +import org.terasology.nui.widgets.UIButton; +import org.terasology.nui.widgets.UILabel; import static org.terasology.engine.identity.storageServiceClient.StatusMessageTranslator.getLocalizedButtonMessage; import static org.terasology.engine.identity.storageServiceClient.StatusMessageTranslator.getLocalizedStatusMessage; @@ -52,7 +28,7 @@ public class PlayerSettingsScreen extends CoreScreenLayer { @In private Context context; @In - private Config config; + private PlayerConfig config; @In private SystemConfig systemConfig; @In @@ -60,55 +36,17 @@ public class PlayerSettingsScreen extends CoreScreenLayer { @In private StorageServiceWorker storageService; - private final List colors = CieCamColors.L65C65; - - /** - * Remove language x from this languagesExcluded table when it is ready for testing - */ - private final Locale[] languagesExcluded = - {Locale.forLanguageTag("zh"), // TODO: Chinese symbols not yet available - Locale.forLanguageTag("hi"), // TODO: Hindi (Indian) symbols not yet available - Locale.forLanguageTag("ar"), // TODO: Arabic symbols not yet available, no translated entries yet - Locale.forLanguageTag("ko"), // TODO: Korean symbols not yet available - Locale.forLanguageTag("fa")}; // TODO: Farsi (Persian) symbols not yet available - - private UIText nametext; - private UISlider slider; private UILabel storageServiceStatus; private UIButton storageServiceAction; - private UISlider heightSlider; - private UISlider eyeHeightSlider; - private UIImage img; - private UICheckbox discordPresence; - private UIDropdownScrollable language; private StorageServiceWorkerStatus storageServiceWorkerStatus; @Override public void onOpened() { super.onOpened(); - if (nametext != null) { - nametext.setText(config.getPlayer().getName()); - } - if (slider != null) { - Color color = config.getPlayer().getColor(); - slider.bindValue(new NotifyingBinding(findClosestIndex(color))); - } - if (heightSlider != null) { - heightSlider.bindValue(new NotifyingBinding(config.getPlayer().getHeight())); - } - if (eyeHeightSlider != null) { - eyeHeightSlider.bindValue(new NotifyingBinding(config.getPlayer().getEyeHeight())); - } - if (discordPresence != null) { - discordPresence.setChecked(config.getPlayer().isDiscordPresence()); - } - if (language != null) { - language.setSelection(systemConfig.locale.get()); - } - updateImage(); } + @Override public void initialise() { setAnimationSystem(MenuAnimationSystems.createDefaultSwipeAnimation()); @@ -117,62 +55,6 @@ public void initialise() { storageServiceAction = find("storageServiceAction", UIButton.class); updateStorageServiceStatus(); - nametext = find("playername", UIText.class); - if (nametext != null) { - nametext.setTooltipDelay(0); - nametext.bindTooltipString(new ReadOnlyBinding() { - @Override - public String get() { - return validateScreen(); - } - }); - } - img = find("image", UIImage.class); - if (img != null) { - ResourceUrn uri = TextureUtil.getTextureUriForColor(Color.WHITE); - Texture tex = Assets.get(uri, Texture.class).get(); - img.setImage(tex); - } - - slider = find("tone", UISlider.class); - if (slider != null) { - slider.setIncrement(0.01f); - Function constant = Functions.constant(" "); // ensure a certain width - slider.setLabelFunction(constant); - } - - heightSlider = find("height", UISlider.class); - if (heightSlider != null) { - heightSlider.setMinimum(1.5f); - heightSlider.setIncrement(0.1f); - heightSlider.setRange(0.5f); - heightSlider.setPrecision(1); - } - - eyeHeightSlider = find("eye-height", UISlider.class); - if (eyeHeightSlider != null) { - eyeHeightSlider.setMinimum(0.5f); - eyeHeightSlider.setIncrement(0.1f); - eyeHeightSlider.setRange(1f); - eyeHeightSlider.setPrecision(1); - } - - discordPresence = find("discord-presence", UICheckbox.class); - - language = find("language", UIDropdownScrollable.class); - if (language != null) { - SimpleUri menuUri = new SimpleUri("engine:menu"); - TranslationProject menuProject = translationSystem.getProject(menuUri); - List locales = new ArrayList<>(menuProject.getAvailableLocales()); - for (Locale languageExcluded : languagesExcluded) { - locales.remove(languageExcluded); - } - Collections.sort(locales, ((Object o1, Object o2) -> (o1.toString().compareTo(o2.toString())))); - language.setOptions(Lists.newArrayList(locales)); - language.setVisibleOptions(5); // Set maximum number of options visible for scrolling - language.setOptionRenderer(new LocaleRenderer(translationSystem)); - } - WidgetUtil.trySubscribe(this, "close", button -> triggerBackAnimation()); IdentityIOHelper identityIOHelper = new IdentityIOHelper(context); @@ -192,26 +74,6 @@ public String get() { } }); - UIButton okButton = find("ok", UIButton.class); - if (okButton != null) { - okButton.subscribe(button -> { - savePlayerSettings(); - triggerBackAnimation(); - }); - okButton.bindEnabled(new ReadOnlyBinding() { - @Override - public Boolean get() { - return Strings.isNullOrEmpty(validateScreen()); - } - }); - okButton.setTooltipDelay(0); - okButton.bindTooltipString(new ReadOnlyBinding() { - @Override - public String get() { - return validateScreen(); - } - }); - } } @Override @@ -230,117 +92,8 @@ private void updateStorageServiceStatus() { storageServiceWorkerStatus = stat; } - private String validateScreen() { - if (nametext != null) { - if (Strings.isNullOrEmpty(nametext.getText()) || nametext.getText().trim().length() == 0) { - return translationSystem.translate("${engine:menu#missing-name-message}"); - } - if (nametext.getText().trim().length() > 100) { - return translationSystem.translate("${engine:menu#validation-username-max-length}"); - } - } - return null; - } - - private float findClosestIndex(Color color) { - int best = 0; - float minDist = Float.MAX_VALUE; - for (int i = 0; i < colors.size(); i++) { - Color other = colors.get(i); - float dr = other.rf() - color.rf(); - float dg = other.gf() - color.gf(); - float db = other.bf() - color.bf(); - - // there are certainly smarter ways to measure color distance, - // but Euclidean distance is good enough for the purpose - float dist = dr * dr + dg * dg + db * db; - if (dist < minDist) { - minDist = dist; - best = i; - } - } - - float max = colors.size() - 1; - return best / max; - } - - private Color findClosestColor(float findex) { - int index = DoubleMath.roundToInt(findex * (double) (colors.size() - 1), RoundingMode.HALF_UP); - Color color = colors.get(index); - return color; - } - - private void updateImage() { - Color color = getColor(); - if (img != null) { - img.setTint(color); - } - } - - private Color getColor() { - if (slider != null) { - float index = slider.getValue(); - return findClosestColor(index); - } else { - return config.getPlayer().getColor(); - } - } - - private Float getHeight() { - if (heightSlider != null) { - float index = heightSlider.getValue(); - return index; - } else { - return config.getPlayer().getHeight(); - } - } - - private Float getEyeHeight() { - if (eyeHeightSlider != null) { - float index = eyeHeightSlider.getValue(); - return index; - } else { - return config.getPlayer().getEyeHeight(); - } - } - - private void savePlayerSettings() { - Color color = getColor(); - config.getPlayer().setColor(color); - Float height = getHeight(); - config.getPlayer().setHeight(height); - Float eyeHeight = getEyeHeight(); - config.getPlayer().setEyeHeight(eyeHeight); - config.getPlayer().setDiscordPresence(discordPresence.isChecked()); - if (nametext != null) { - config.getPlayer().setName(nametext.getText().trim()); - config.getPlayer().setHasEnteredUsername(true); - } - if (!systemConfig.locale.get().equals(language.getSelection())) { - systemConfig.locale.set(language.getSelection()); - getManager().invalidate(); - } - } - @Override public boolean isLowerLayerVisible() { return false; } - - /** - * Calls update() in parent class when the slider value changes - */ - private final class NotifyingBinding extends DefaultBinding { - - private NotifyingBinding(Float value) { - super(value); - } - - @Override - public void set(Float v) { - super.set(v); - - updateImage(); - } - } } diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java index 27ad5eb6e4..65e9b51e65 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java +++ b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java @@ -7,6 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.config.RenderingConfig; import org.terasology.engine.context.Context; import org.terasology.engine.core.GameEngine; @@ -22,26 +23,26 @@ import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.logic.players.LocalPlayerSystem; +import org.terasology.engine.rendering.ShaderManager; import org.terasology.engine.rendering.assets.material.Material; import org.terasology.engine.rendering.backdrop.BackdropProvider; import org.terasology.engine.rendering.cameras.OpenVRStereoCamera; import org.terasology.engine.rendering.cameras.PerspectiveCamera; import org.terasology.engine.rendering.cameras.SubmersibleCamera; -import org.terasology.engine.rendering.opengl.FBO; -import org.terasology.engine.rendering.opengl.ScreenGrabber; -import org.terasology.engine.rendering.opengl.fbms.DisplayResolutionDependentFbo; -import org.terasology.engine.rendering.openvrprovider.OpenVRProvider; -import org.terasology.engine.rendering.world.viewDistance.ViewDistance; -import org.terasology.math.TeraMath; -import org.terasology.engine.rendering.ShaderManager; import org.terasology.engine.rendering.dag.ModuleRendering; import org.terasology.engine.rendering.dag.Node; import org.terasology.engine.rendering.dag.RenderGraph; import org.terasology.engine.rendering.dag.RenderPipelineTask; import org.terasology.engine.rendering.dag.RenderTaskListGenerator; import org.terasology.engine.rendering.dag.stateChanges.SetViewportToSizeOf; +import org.terasology.engine.rendering.opengl.FBO; +import org.terasology.engine.rendering.opengl.ScreenGrabber; +import org.terasology.engine.rendering.opengl.fbms.DisplayResolutionDependentFbo; +import org.terasology.engine.rendering.openvrprovider.OpenVRProvider; +import org.terasology.engine.rendering.world.viewDistance.ViewDistance; import org.terasology.engine.utilities.Assets; import org.terasology.engine.world.WorldProvider; +import org.terasology.math.TeraMath; import java.util.List; @@ -139,7 +140,7 @@ public WorldRendererImpl(Context context, GLBufferPool bufferPool) { * in match. */ vrProvider.getState().setGroundPlaneYOffset( - GROUND_PLANE_HEIGHT_DISPARITY - context.get(Config.class).getPlayer().getEyeHeight()); + GROUND_PLANE_HEIGHT_DISPARITY - context.get(PlayerConfig.class).eyeHeight.get()); currentRenderingStage = RenderingStage.LEFT_EYE; } else { playerCamera = new PerspectiveCamera(worldProvider, renderingConfig, context.get(DisplayDevice.class)); diff --git a/engine/src/main/java/org/terasology/engine/telemetry/metrics/GameConfigurationMetric.java b/engine/src/main/java/org/terasology/engine/telemetry/metrics/GameConfigurationMetric.java index 94f2914811..ea20763760 100644 --- a/engine/src/main/java/org/terasology/engine/telemetry/metrics/GameConfigurationMetric.java +++ b/engine/src/main/java/org/terasology/engine/telemetry/metrics/GameConfigurationMetric.java @@ -1,18 +1,5 @@ -/* - * Copyright 2017 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.telemetry.metrics; import com.snowplowanalytics.snowplow.tracker.events.Unstructured; @@ -97,8 +84,8 @@ private void fetchConfig() { SystemConfig systemConfig = context.get(SystemConfig.class); language = systemConfig.locale.get().toString(); - PlayerConfig playerConfig = config.getPlayer(); - playerHeight = playerConfig.getHeight(); - playerEyeHeight = playerConfig.getEyeHeight(); + PlayerConfig playerConfig = context.get(PlayerConfig.class); + playerHeight = playerConfig.height.get(); + playerEyeHeight = playerConfig.eyeHeight.get(); } } diff --git a/engine/src/main/resources/assets/i18n/menu.lang b/engine/src/main/resources/assets/i18n/menu.lang index b0cb9cdafc..e920c01751 100644 --- a/engine/src/main/resources/assets/i18n/menu.lang +++ b/engine/src/main/resources/assets/i18n/menu.lang @@ -113,6 +113,7 @@ "dialog-yes": "dialog-yes", "disable-all-modules": "disable-all-modules", "discord-presence": "discord-presence", + "discord-settings-title": "discord-settings-title", "drop-item": "drop-item", "download-module": "download-module", "downloading-server-list": "downloading-server-list", diff --git a/engine/src/main/resources/assets/i18n/menu_en.lang b/engine/src/main/resources/assets/i18n/menu_en.lang index 77fe9e52f8..46e6c5e14c 100644 --- a/engine/src/main/resources/assets/i18n/menu_en.lang +++ b/engine/src/main/resources/assets/i18n/menu_en.lang @@ -117,6 +117,7 @@ "disable-launch-popup": "Remember and don't show again", "disable-rendering-class": "Disable", "discord-presence": "Discord Rich Presence", + "discord-settings-title": "Discord Settings", "download-module": "Download", "downloading-server-list": "Downloading server list ..", "drop-item": "Drop Item", diff --git a/engine/src/main/resources/assets/ui/config/autoConfigScreen.ui b/engine/src/main/resources/assets/ui/config/autoConfigScreen.ui index 552fc1353c..806e915516 100644 --- a/engine/src/main/resources/assets/ui/config/autoConfigScreen.ui +++ b/engine/src/main/resources/assets/ui/config/autoConfigScreen.ui @@ -5,26 +5,42 @@ "type": "RelativeLayout", "contents": [ { - "type": "ColumnLayout", - "id": "mainContainer", - "fillVerticalSpace": false, - "layoutInfo": { - "use-content-height": true, - "position-horizontal-center": {} + "type": "ScrollableArea", + "content": { + "type": "ColumnLayout", + "id": "mainContainer", + "fillVerticalSpace": false, + "layoutInfo": { + "use-content-height": true, + "position-horizontal-center": {} + }, + "contents": [] }, - "contents": [] + "layoutInfo": { + "width": 800, + "position-horizontal-center": {}, + "position-top": { + "target": "TOP", + "offset": 48 + }, + "position-bottom": { + "target": "TOP", + "offset": 32, + "widget": "close" + } + } }, { "type": "UIButton", "text": "${engine:menu#back}", "id": "close", "layoutInfo": { - "height": 32, - "width": 200, - "position-horizontal-center": {}, - "position-bottom": { - "target": "BOTTOM", - "offset": 48 + "height": 32, + "width": 200, + "position-horizontal-center": {}, + "position-bottom": { + "target": "BOTTOM", + "offset": 48 } } } diff --git a/engine/src/main/resources/assets/ui/config/colorPickerWidget.ui b/engine/src/main/resources/assets/ui/config/colorPickerWidget.ui new file mode 100644 index 0000000000..b7161c420e --- /dev/null +++ b/engine/src/main/resources/assets/ui/config/colorPickerWidget.ui @@ -0,0 +1,15 @@ +{ + "type": "ColumnLayout", + "horizontalSpacing": 10, + "contents": [ + { + "type": "UISlider", + "id": "tone" + }, + { + "type": "UIImage", + "skin": "framed_image", + "id": "image" + } + ] +} diff --git a/engine/src/main/resources/assets/ui/menu/playerMenuScreen.ui b/engine/src/main/resources/assets/ui/menu/playerMenuScreen.ui index 0a0167949c..d075015c93 100644 --- a/engine/src/main/resources/assets/ui/menu/playerMenuScreen.ui +++ b/engine/src/main/resources/assets/ui/menu/playerMenuScreen.ui @@ -32,36 +32,6 @@ "horizontalSpacing": 8, "overrideChildEnabledProp" : false, "contents": [ - { - "type": "UILabel", - "text": "${engine:menu#player-name}:" - }, - { - "type": "UIText", - "id": "playername" - }, - { - "type": "UILabel", - "text": "${engine:menu#player-color}:" - }, - { - "type": "RowLayout", - "horizontalSpacing": 10, - "contents": [ - { - "type": "UISlider", - "id": "tone", - "layoutInfo": { - "relativeWidth": 0.88 - } - }, - { - "type": "UIImage", - "skin": "framed_image", - "id": "image" - } - ] - }, { "type": "UILabel", "text": "Multiplayer identities:", @@ -108,64 +78,6 @@ } ] }, - { - "type": "UILabel", - "text": "${engine:menu#settings-language}:" - }, - { - "type": "UIDropdownScrollable", - "id": "language" - }, - { - "type": "UILabel", - "text": "${engine:menu#experimental}" - }, - { - "type": "UILabel", - "text": "" - }, - { - "type": "UILabel", - "text": "${engine:menu#player-height}:" - }, - { - "type": "RowLayout", - "horizontalSpacing": 10, - "contents": [ - { - "type": "UISlider", - "id": "height" - } - ] - }, - { - "type": "UILabel", - "text": "${engine:menu#player-eye-height}:" - }, - { - "type": "RowLayout", - "horizontalSpacing": 10, - "contents": [ - { - "type": "UISlider", - "id": "eye-height" - } - ] - }, - { - "type": "UILabel", - "text": "${engine:menu#discord-presence}:" - }, - { - "type": "RowLayout", - "horizontalSpacing": 10, - "contents": [ - { - "type": "UICheckbox", - "id": "discord-presence" - } - ] - } ] }, "layoutInfo": { diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordAutoConfig.java b/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordAutoConfig.java new file mode 100644 index 0000000000..1d4f80bd58 --- /dev/null +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordAutoConfig.java @@ -0,0 +1,27 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.subsystem.discordrpc; + +import org.terasology.engine.config.flexible.AutoConfig; +import org.terasology.engine.config.flexible.Setting; + +import static org.terasology.engine.config.flexible.SettingArgument.defaultValue; +import static org.terasology.engine.config.flexible.SettingArgument.name; +import static org.terasology.engine.config.flexible.SettingArgument.type; + +public class DiscordAutoConfig extends AutoConfig { + + public final Setting discordPresence = + setting( + type(Boolean.class), + defaultValue(true), + name("${engine:menu#discord-presence}") + ); + + + @Override + public String getName() { + return "${engine:menu#discord-settings-title}"; + } +} diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordRPCSubSystem.java b/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordRPCSubSystem.java index f80eb65d0b..a8a4914bfd 100644 --- a/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordRPCSubSystem.java +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/subsystem/discordrpc/DiscordRPCSubSystem.java @@ -1,45 +1,28 @@ -/* - * Copyright 2018 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.subsystem.discordrpc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terasology.engine.config.Config; -import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.context.Context; import org.terasology.engine.core.GameEngine; import org.terasology.engine.core.subsystem.EngineSubsystem; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.time.OffsetDateTime; /** - * Subsystem that manages Discord RPC in the game client, such as status or connection. - * This subsystem can be enhanced further to improve game presentation in rich presence. - * + * Subsystem that manages Discord RPC in the game client, such as status or connection. This subsystem can be enhanced + * further to improve game presentation in rich presence. + *

* It communicates with the thread safely using thread-safe shared buffer. * * @see EngineSubsystem */ -public final class DiscordRPCSubSystem implements EngineSubsystem, PropertyChangeListener { +public final class DiscordRPCSubSystem implements EngineSubsystem { private static final Logger logger = LoggerFactory.getLogger(DiscordRPCSubSystem.class); private static DiscordRPCSubSystem instance; - private Config config; + private DiscordAutoConfig config; private DiscordRPCThread thread; public DiscordRPCSubSystem() throws IllegalStateException { @@ -50,58 +33,10 @@ public DiscordRPCSubSystem() throws IllegalStateException { instance = this; } - @Override - public void initialise(GameEngine engine, Context rootContext) { - logger.info("Initializing..."); - - thread = new DiscordRPCThread(); - thread.getBuffer().setState("In Main Menu"); - - config = rootContext.get(Config.class); - - if (config.getPlayer().isDiscordPresence()) { - thread.enable(); - } else { - logger.info("Discord RPC is disabled! No connection is being made during initialization."); - thread.disable(); - } - thread.start(); - } - - @Override - public synchronized void postInitialise(Context context) { - config = context.get(Config.class); - config.getPlayer().subscribe(this); - - if (config.getPlayer().isDiscordPresence()) { - thread.enable(); - } else { - thread.disable(); - } - } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(PlayerConfig.DISCORD_PRESENCE)) { - thread.setEnabled((boolean) evt.getNewValue()); - } - } - - @Override - public synchronized void preShutdown() { - thread.disable(); - thread.stop(); - } - - @Override - public String getName() { - return "DiscordRPC"; - } - /** - * Re-discovers the discord ipc in case the player started the discord client after running the game. - * And, the re-connecting process failed to connect. - * + * Re-discovers the discord ipc in case the player started the discord client after running the game. And, the + * re-connecting process failed to connect. + *

* This should be called once by {@link DiscordRPCSystem} */ public static void discover() { @@ -117,6 +52,7 @@ public static void reset() { /** * Sets the name of the gameplay the player is playing (e.g. Custom, Josharias Survival, etc...) + * * @param name the name of the gameplay */ public static void setGameplayName(String name) { @@ -163,4 +99,45 @@ public static void resetPartyInfo() { private static DiscordRPCSubSystem getInstance() { return instance; } + + @Override + public void initialise(GameEngine engine, Context rootContext) { + logger.info("Initializing..."); + + thread = new DiscordRPCThread(); + thread.getBuffer().setState("In Main Menu"); + + config = rootContext.get(DiscordAutoConfig.class); + + if (config.discordPresence.get()) { + thread.enable(); + } else { + logger.info("Discord RPC is disabled! No connection is being made during initialization."); + thread.disable(); + } + thread.start(); + } + + @Override + public synchronized void postInitialise(Context context) { + config = context.get(DiscordAutoConfig.class); + config.discordPresence.subscribe((setting, old) -> { + if (setting.get()) { + thread.enable(); + } else { + thread.disable(); + } + }); + } + + @Override + public synchronized void preShutdown() { + thread.disable(); + thread.stop(); + } + + @Override + public String getName() { + return "DiscordRPC"; + } }