Merge remote-tracking branch 'origin/nui-gestalt-separation' into feature/migrate-gestalt-v7

engine/src/main/java/org/terasology/engine/config/flexible/ui/AutoConfigWidgetFactory.java
engine/src/main/java/org/terasology/engine/config/flexible/ui/SettingWidgetFactory.java
engine/src/main/java/org/terasology/engine/logic/players/LocalPlayerSystem.java
engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java
engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/EnterUsernamePopup.java
engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java
engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/SelectionScreen.java
engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/settings/PlayerSettingsScreen.java
develop
Kevin Turner 2021-04-02 09:22:19 -07:00
commit 2f9d4afce7
37 changed files with 688 additions and 657 deletions

View File

@ -26,8 +26,8 @@ class SettingWidgetFactoryTest {
.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<Integer> setting = mock(Setting.class);

View File

@ -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;
@ -93,10 +80,6 @@ public final class Config {
return config.getNetwork();
}
public PlayerConfig getPlayer() {
return config.getPlayer();
}
public RenderingConfig getRendering() {
return config.getRendering();
}

View File

@ -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<String> playerName = setting(
type(String.class),
defaultValue(defaultPlayerName()),
name("${engine:menu#player-name}"),
constraint(new StringConstraint(
StringConstraint.notEmptyOrNull(),
StringConstraint.maxLength(100))
)
);
public final Setting<Color> color = setting(
type(Color.class),
defaultValue(defaultPlayerColor()),
name("${engine:menu#player-color}"),
constraint(new ColorConstraint())
);
public final Setting<Float> 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<Float> 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 @@ public class PlayerConfig extends AbstractSubscribable {
List<Color> colors = CieCamColors.L65C65;
return colors.get(rng.nextInt(colors.size()));
}
@Override
public String getName() {
return "${engine:menu#player-settings-title}";
}
}

View File

@ -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 @@ import java.util.Map;
* 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 final class RootConfig {
return network;
}
public PlayerConfig getPlayer() {
return player;
}
public RenderingConfig getRendering() {
return rendering;
}

View File

@ -5,6 +5,7 @@ package org.terasology.engine.config;
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 java.util.Optional;
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<Long> 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<Integer> 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<Integer> 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<Integer> maxUnloadedChunksPercentageTillSave = setting(
type(Integer.class),
defaultValue(40),
name("Max unloaded chunks percentage till save"),
constraint(new NumberRangeConstraint<>(0, 100, false, false))
);
public final Setting<Boolean> debugEnabled = setting(
type(Boolean.class),
defaultValue(false)
defaultValue(false),
name("Debug mode")
);
public final Setting<Boolean> monitoringEnabled = setting(
type(Boolean.class),
defaultValue(false)
defaultValue(false),
name("Monitoring")
);
public final Setting<Boolean> 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<Long> 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> 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

View File

@ -32,7 +32,7 @@ import java.util.Set;
public class AutoConfigManager {
private static final Logger logger = LoggerFactory.getLogger(AutoConfigManager.class);
private final Set<AutoConfig> loadedConfigs = Sets.newHashSet();
private final Set<AutoConfig> loadedConfigs = Sets.newLinkedHashSet();
private final Serializer<?> serializer;
public AutoConfigManager(Serializer<?> serializer) {
@ -109,7 +109,7 @@ public class AutoConfigManager {
// 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<AutoConfig>)config.getClass()), output);
} catch (IOException e) {
logger.error("Error while saving config {} to disk", config.getId(), e);
}

View File

@ -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<T, R> implements Binding<T> {
private final Binding<R> internalBinging;
private final Function<T, R> setMapping;
private final Function<R, T> getMapping;
public MappingBinding(Binding<R> internalBinging, Function<T, R> setMapping, Function<R, T> 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));
}
}

View File

@ -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<T> implements Binding<T> {
private final Setting<T> setting;
public SettingBinding(Setting<T> setting) {
this.setting = setting;
}
@Override
public T get() {
return setting.get();
}
@Override
public void set(T value) {
setting.set(value);
}
}

View File

@ -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<Color> {
@Override
public boolean isSatisfiedBy(Color value) {
return true;
}
@Override
public void warnUnsatisfiedBy(Color value) {
}
}

View File

@ -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<Locale> {
private static final Logger logger = LoggerFactory.getLogger(LocaleConstraint.class);
private final Set<Locale> locales;
public LocaleConstraint(Set<Locale> 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(",", "[", "]"))
);
}
}

View File

@ -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<T> implements Predicate<T> {
private final Predicate<T> predicate;
private final String description;
public PredicateWithDescription(String description, Predicate<T> predicate) {
this.predicate = predicate;
this.description = description;
}
public String getDescription() {
return description;
}
@Override
public boolean test(T s) {
return predicate.test(s);
}
}

View File

@ -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<String> {
private static final Logger logger = LoggerFactory.getLogger(StringConstraint.class);
private final List<Predicate<String>> predicates;
public StringConstraint(Predicate<String>... predicates) {
this.predicates = Arrays.asList(predicates);
}
public static Predicate<String> notEmptyOrNull() {
return new PredicateWithDescription<>("not null or not empty", Predicates.not(Strings::isNullOrEmpty));
}
public static Predicate<String> maxLength(int length) {
return new PredicateWithDescription<>("length should be less than " + length, s -> s.length() < length);
}
public static Predicate<String> 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<String> p) {
if (p instanceof PredicateWithDescription) {
return ((PredicateWithDescription<String>) 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(",", "[", "]")));
}
}

View File

@ -9,14 +9,14 @@ import org.terasology.gestalt.assets.management.AssetManager;
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 class AutoConfigScreen extends CoreScreenLayer {
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());

View File

@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@ -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 class AutoConfigWidgetFactory implements TypeWidgetFactory {
Optional<UIWidget> 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;
}

View File

@ -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.gestalt.assets.ResourceUrn;
import org.terasology.gestalt.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<Color, ColorConstraint> {
@In
private AssetManager assetManager;
private final List<Color> 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<Object, String> 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;
}
}

View File

@ -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<T> extends ConstraintWidgetFactory<T, SettingConstraint<T>> {
private final Context context;
public DefaultConstraintWidgetFactory(Context context) {
this.context = context;
}
@Override
protected Optional<UIWidget> buildWidget() {
Setting<T> setting = getSetting();
Binding<T> binding = new Binding<T>() {
@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());
}
}

View File

@ -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<Locale, LocaleConstraint> {
/**
* 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<UIWidget> buildWidget() {
Setting<Locale> setting = getSetting();
Binding<Locale> binding = new Binding<Locale>() {
@Override
public Locale get() {
return setting.get();
}
@Override
public void set(Locale value) {
setting.set(value);
}
};
UIDropdownScrollable<Locale> dropdownScrollable = new UIDropdownScrollable<>();
SimpleUri menuUri = new SimpleUri("engine:menu");
TranslationProject menuProject = translationSystem.getProject(menuUri);
List<Locale> locales = new ArrayList<>(menuProject.getAvailableLocales());
for (Locale languageExcluded : languagesExcluded) {
locales.remove(languageExcluded);
}
Collections.sort(locales, (Comparator.comparing((Function<Object, String>) 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);
}
}

View File

@ -1,16 +1,17 @@
// Copyright 2020 The Terasology Foundation
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0
package org.terasology.engine.config.flexible.ui;
import com.google.common.collect.ImmutableMap;
import org.terasology.gestalt.assets.management.AssetManager;
import org.terasology.engine.config.flexible.Setting;
import org.terasology.engine.config.flexible.constraints.SettingConstraint;
import org.terasology.gestalt.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.gestalt.assets.management.AssetManager;
import org.terasology.gestalt.module.ModuleEnvironment;
import org.terasology.nui.UIWidget;
import java.lang.reflect.Type;
import java.util.Optional;
@ -29,10 +30,12 @@ import java.util.Optional;
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 class SettingWidgetFactory {
*/
public <T> Optional<UIWidget> createWidgetFor(Setting<T> setting) {
return getConstraintWidgetFactory(setting)
.flatMap(factory -> factory.buildWidgetFor(setting));
.orElseGet(()-> new DefaultConstraintWidgetFactory<>(context))
.buildWidgetFor(setting);
}
/**
@ -55,22 +59,23 @@ public class SettingWidgetFactory {
*/
<T> Optional<ConstraintWidgetFactory<T, ?>> getConstraintWidgetFactory(Setting<T> setting) {
SettingConstraint<?> constraint = setting.getConstraint();
if (constraint != null) {
for (Class<? extends ConstraintWidgetFactory> widgetType
: environment.getSubtypesOf(ConstraintWidgetFactory.class)) {
Type constraintType =
ReflectionUtil.getTypeParameterForSuper(widgetType, ConstraintWidgetFactory.class, 1);
for (Class<? extends ConstraintWidgetFactory> 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<T, ?> factory = widgetType.newInstance();
ConstraintWidgetFactory<T, ?> 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();
}
}

View File

@ -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 class SetupLocalPlayer extends SingleStepLoadProcess {
@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;
}

View File

@ -25,8 +25,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
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 class TranslationSystemImpl implements TranslationSystem {
systemConfig = context.get(SystemConfig.class);
assetManager = context.get(AssetManager.class);
refresh();
}

View File

@ -5,7 +5,7 @@ package org.terasology.engine.logic.characters;
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 class GazeAuthoritySystem extends BaseComponentSystem {
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());
}

View File

@ -7,6 +7,7 @@ import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
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;
@ -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 @@ public class LocalPlayerSystem extends BaseComponentSystem implements UpdateSubs
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.

View File

@ -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,6 +9,7 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
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.Time;
import org.terasology.engine.core.module.ModuleManager;
@ -289,12 +290,13 @@ public class ClientConnectionHandler extends ChannelInboundHandlerAdapter {
*/
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());
}

View File

@ -4,33 +4,30 @@ package org.terasology.engine.rendering.nui.layers.mainMenu;
import com.google.common.base.Strings;
import org.terasology.gestalt.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<String>() {
@Override
public String get() {
@ -41,8 +38,7 @@ public class EnterUsernamePopup extends CoreScreenLayer {
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<Boolean>() {
@ -61,7 +57,6 @@ public class EnterUsernamePopup extends CoreScreenLayer {
}
WidgetUtil.trySubscribe(this, "cancel", button -> {
playerConfig.setHasEnteredUsername(true);
getManager().popScreen();
});
}
@ -70,7 +65,7 @@ public class EnterUsernamePopup extends CoreScreenLayer {
public void onOpened() {
super.onOpened();
if (username != null) {
username.setText(config.getPlayer().getName());
username.setText(playerConfig.playerName.get());
}
}

View File

@ -8,6 +8,7 @@ import com.google.common.collect.Collections2;
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.ServerInfo;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.GameThread;
@ -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 class JoinGameScreen extends CoreScreenLayer {
infoService = new ServerInfoService();
if (!config.getPlayer().hasEnteredUsername()) {
if (playerConfig.playerName.getDefaultValue().equals(playerConfig.playerName.get())) {
getManager().pushScreen(EnterUsernamePopup.ASSET_URI, EnterUsernamePopup.class);
}

View File

@ -9,15 +9,15 @@ import org.terasology.engine.core.GameEngine;
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 class SelectGameScreen extends SelectionScreen {
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);
}

View File

@ -6,25 +6,26 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.gestalt.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.gestalt.naming.Name;
import org.terasology.gestalt.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;

View File

@ -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.gestalt.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<Color> 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<Locale> 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 class PlayerSettingsScreen extends CoreScreenLayer {
storageServiceAction = find("storageServiceAction", UIButton.class);
updateStorageServiceStatus();
nametext = find("playername", UIText.class);
if (nametext != null) {
nametext.setTooltipDelay(0);
nametext.bindTooltipString(new ReadOnlyBinding<String>() {
@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<Object, String> 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<Locale> 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 class PlayerSettingsScreen extends CoreScreenLayer {
}
});
UIButton okButton = find("ok", UIButton.class);
if (okButton != null) {
okButton.subscribe(button -> {
savePlayerSettings();
triggerBackAnimation();
});
okButton.bindEnabled(new ReadOnlyBinding<Boolean>() {
@Override
public Boolean get() {
return Strings.isNullOrEmpty(validateScreen());
}
});
okButton.setTooltipDelay(0);
okButton.bindTooltipString(new ReadOnlyBinding<String>() {
@Override
public String get() {
return validateScreen();
}
});
}
}
@Override
@ -230,117 +92,8 @@ public class PlayerSettingsScreen extends CoreScreenLayer {
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<Float> {
private NotifyingBinding(Float value) {
super(value);
}
@Override
public void set(Float v) {
super.set(v);
updateImage();
}
}
}

View File

@ -7,6 +7,7 @@ import org.joml.Vector3ic;
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;
@ -139,7 +140,7 @@ public final class WorldRendererImpl implements WorldRenderer {
* 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));

View File

@ -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 @@ public final class GameConfigurationMetric extends Metric {
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();
}
}

View File

@ -0,0 +1,15 @@
{
"type": "ColumnLayout",
"horizontalSpacing": 10,
"contents": [
{
"type": "UISlider",
"id": "tone"
},
{
"type": "UIImage",
"skin": "framed_image",
"id": "image"
}
]
}

View File

@ -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",

View File

@ -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",

View File

@ -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
}
}
}

View File

@ -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": {

View File

@ -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<Boolean> discordPresence =
setting(
type(Boolean.class),
defaultValue(true),
name("${engine:menu#discord-presence}")
);
@Override
public String getName() {
return "${engine:menu#discord-settings-title}";
}
}

View File

@ -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.
* <p>
* 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 final class DiscordRPCSubSystem implements EngineSubsystem, PropertyChang
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.
* <p>
* This should be called once by {@link DiscordRPCSystem}
*/
public static void discover() {
@ -117,6 +52,7 @@ public final class DiscordRPCSubSystem implements EngineSubsystem, PropertyChang
/**
* 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 final class DiscordRPCSubSystem implements EngineSubsystem, PropertyChang
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";
}
}