Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: basic towny integration #334

Merged
merged 28 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
74d6870
beginning work on towny support
broccolai Nov 5, 2023
e5e6e05
improve editorconfig for imports
broccolai Nov 5, 2023
64142ae
feature: add special handlers to registry, move integrations
broccolai Nov 5, 2023
f53fddb
move integration base type into common module
broccolai Nov 5, 2023
a3aef52
feature: add alliance chat for towny
broccolai Nov 5, 2023
e29bcba
add some default formatting for towny channels
broccolai Nov 5, 2023
6f04fc8
Add integration configs
jpenilla Nov 6, 2023
2a4da56
simplify special config channel registration
jpenilla Nov 7, 2023
cb18955
Fix IntegrationConfigContainer.Serializer always moving its node to t…
jpenilla Nov 8, 2023
3a94772
add CarbonPlatformModule
broccolai Nov 8, 2023
098e970
format
broccolai Nov 8, 2023
965d37f
format again
broccolai Nov 8, 2023
f6b2673
Merge branch 'trunk' into feat/towny
broccolai Nov 8, 2023
dc81d75
Merge branch 'trunk' into feat/towny
broccolai Nov 8, 2023
e6dcf95
fix: format again again
broccolai Nov 8, 2023
f978822
fix: modify how CarbonPlatformModule works
broccolai Nov 9, 2023
66a1d62
fix: specify an identifier for resident list channels
broccolai Nov 9, 2023
9528149
Merge branch 'trunk' into feat/towny
jpenilla Nov 9, 2023
2530bce
Add Towny as soft depend
jpenilla Nov 9, 2023
88232e5
Move messages to locale
jpenilla Nov 9, 2023
1bd68c4
Merge branch 'trunk' into feat/towny
jpenilla Nov 9, 2023
c28ac70
sort integration configs
jpenilla Nov 10, 2023
8612059
fix
jpenilla Nov 10, 2023
dee281f
fix 2
jpenilla Nov 10, 2023
7fd821d
adjust towny channel default formats
jpenilla Nov 10, 2023
79641ac
Add `@ConfigHeader`
jpenilla Nov 10, 2023
adb753e
fix
jpenilla Nov 10, 2023
d1cddd0
disable alliance chat by default
jpenilla Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,10 @@ indent_style = space
insert_final_newline = true
max_line_length = off

[*.java]
ij_java_imports_layout = *, |, $*
ij_java_class_count_to_use_import_on_demand = 999
ij_java_names_count_to_use_import_on_demand = 999

[{*.kt,*.kts,*.yml}]
indent_size = 2
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import net.draycia.carbon.common.command.CarbonCommand;
import net.draycia.carbon.common.command.ExecutionCoordinatorHolder;
import net.draycia.carbon.common.config.ConfigManager;
import net.draycia.carbon.common.integration.Integration;
import net.draycia.carbon.common.listeners.Listener;
import net.draycia.carbon.common.messages.CarbonMessages;
import net.draycia.carbon.common.messaging.MessagingManager;
Expand Down Expand Up @@ -122,6 +123,17 @@ protected void init() {
TimeUnit.SECONDS
);

// Integration
final Set<Integration> integrations = this.injector().getInstance(Key.get(new TypeLiteral<>() {}));

for (final Integration integration : integrations) {
if (!integration.eligible()) {
continue;
}

integration.register();
}

// Load channels
this.channelRegistry().loadConfigChannels(this.carbonMessages);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* CarbonChat
*
* Copyright (c) 2023 Josua Parks (Vicarious)
* Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.draycia.carbon.common;

import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import net.draycia.carbon.common.integration.Integration;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;

@DefaultQualifier(NonNull.class)
public abstract class CarbonPlatformModule extends AbstractModule {

@Override
protected void configure() {
this.configureIntegrations(
Multibinder.newSetBinder(this.binder(), Integration.class),
Multibinder.newSetBinder(this.binder(), Integration.ConfigMeta.class)
);
}

protected void configureIntegrations(
final Multibinder<Integration> integrations,
final Multibinder<Integration.ConfigMeta> configs
) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
import net.draycia.carbon.api.channels.ChannelRegistry;
import net.draycia.carbon.api.channels.ChatChannel;
import net.draycia.carbon.api.event.CarbonEventHandler;
Expand Down Expand Up @@ -74,15 +77,20 @@
@DefaultQualifier(NonNull.class)
public class CarbonChannelRegistry extends ChatListenerInternal implements ChannelRegistry {

private static final String PARTYCHAT_CONF = "partychat.conf";

private final Path configChannelDir;
private final Injector injector;
private final Logger logger;
private final ConfigManager config;
private @MonotonicNonNull Key defaultKey;
private final CarbonMessages carbonMessages;
private final CarbonEventHandler eventHandler;
private final Map<String, SpecialHandler<?>> handlers = new HashMap<>();

private record SpecialHandler<T extends ConfigChatChannel>(Class<T> cls, Supplier<T> defaultSupplier) {}

public <T extends ConfigChatChannel> void registerSpecialConfigChannel(final String fileName, final Class<T> type) {
this.handlers.put(fileName, new SpecialHandler<>(type, () -> this.injector.getInstance(type)));
}

private volatile Registry<Key, ChatChannel> channelRegistry = Registry.create();
private final Set<Key> configChannels = ConcurrentHashMap.newKeySet();
Expand All @@ -106,6 +114,10 @@ public CarbonChannelRegistry(
this.carbonMessages = carbonMessages;
this.eventHandler = events;

if (config.primaryConfig().partyChat().enabled) {
this.registerSpecialConfigChannel(PartyChatChannel.FILE_NAME, PartyChatChannel.class);
}

events.subscribe(CarbonReloadEvent.class, -99, true, event -> this.reloadConfigChannels());
}

Expand Down Expand Up @@ -204,14 +216,10 @@ private void loadConfigChannels_(final CarbonMessages messages) {
this.logger.info("Loading config channels...");
this.defaultKey = this.config.primaryConfig().defaultChannel();

final boolean party = this.config.primaryConfig().partyChat().enabled;
if (party) {
this.saveDefaultPartyConfig();
}
this.saveSpecialDefaults();

List<Path> channelConfigs = FileUtil.listDirectoryEntries(this.configChannelDir, "*.conf");
if (channelConfigs.isEmpty() ||
party && channelConfigs.size() == 1 && channelConfigs.get(0).getFileName().toString().equals(PARTYCHAT_CONF)) {
if (channelConfigs.size() == this.handlers.size()) {
this.saveDefaultChannelConfig();
channelConfigs = FileUtil.listDirectoryEntries(this.configChannelDir, "*.conf");
}
Expand Down Expand Up @@ -251,6 +259,24 @@ private void loadConfigChannels_(final CarbonMessages messages) {
this.logger.info("Registered channels: [" + channels + "]");
}

private void saveSpecialDefaults() {
for (final Map.Entry<String, SpecialHandler<?>> e : this.handlers.entrySet()) {
final Path configFile = this.configChannelDir.resolve(e.getKey());
if (Files.isRegularFile(configFile)) {
continue;
}
try {
final ConfigChatChannel configChannel = e.getValue().defaultSupplier().get();
final ConfigurationLoader<?> loader = this.config.configurationLoader(FileUtil.mkParentDirs(configFile));
final ConfigurationNode node = loader.createNode();
node.set(e.getValue().cls(), configChannel);
loader.save(node);
} catch (final IOException exception) {
throw Exceptions.rethrow(exception);
}
}
}

private void saveDefaultChannelConfig() {
try {
final Path configFile = this.configChannelDir.resolve("global.conf");
Expand All @@ -264,29 +290,12 @@ private void saveDefaultChannelConfig() {
}
}

private void saveDefaultPartyConfig() {
final Path configFile = this.configChannelDir.resolve(PARTYCHAT_CONF);
if (Files.isRegularFile(configFile)) {
return;
}
try {
final ConfigChatChannel configChannel = this.injector.getInstance(PartyChatChannel.class);
final ConfigurationLoader<?> loader = this.config.configurationLoader(FileUtil.mkParentDirs(configFile));
final ConfigurationNode node = loader.createNode();
node.set(PartyChatChannel.class, configChannel);
loader.save(node);
} catch (final IOException exception) {
throw Exceptions.rethrow(exception);
}
}

private @Nullable ChatChannel loadChannel(final Path channelFile) {
final ConfigurationLoader<?> loader = this.config.configurationLoader(channelFile);

try {
final Class<? extends ConfigChatChannel> type = this.config.primaryConfig().partyChat().enabled && channelFile.getFileName().toString().equals(PARTYCHAT_CONF)
? PartyChatChannel.class
: ConfigChatChannel.class;
final @Nullable SpecialHandler<?> special = this.handlers.get(channelFile.getFileName().toString());
final Class<? extends ConfigChatChannel> type = special == null ? ConfigChatChannel.class : special.cls();

final ConfigurationNode loaded = updateNode(loader.load());
final @Nullable ConfigChatChannel channel = loaded.get(type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ public class ConfigChatChannel implements ChatChannel {
@Override
public boolean shouldRegisterCommands() {
return Objects.requireNonNullElse(this.shouldRegisterCommands, true);

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
@DefaultQualifier(NonNull.class)
public class PartyChatChannel extends ConfigChatChannel {

public static final String FILE_NAME = "partychat.conf";

public PartyChatChannel() {
this.key = Key.key("carbon", "partychat");
this.messageSource = new ConfigChannelMessageSource();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
import java.nio.file.Path;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import net.draycia.carbon.api.event.CarbonEventHandler;
import net.draycia.carbon.common.DataDirectory;
import net.draycia.carbon.common.command.CommandSettings;
import net.draycia.carbon.common.event.events.CarbonReloadEvent;
import net.draycia.carbon.common.integration.Integration;
import net.draycia.carbon.common.serialisation.gson.LocaleSerializerConfigurate;
import net.draycia.carbon.common.util.FileUtil;
import net.kyori.adventure.key.Key;
Expand Down Expand Up @@ -57,6 +59,7 @@ public final class ConfigManager {
private final Path dataDirectory;
private final LocaleSerializerConfigurate locale;
private final Logger logger;
private final Set<Integration.ConfigMeta> integrations;

private volatile @MonotonicNonNull PrimaryConfig primaryConfig = null;

Expand All @@ -65,11 +68,13 @@ private ConfigManager(
final CarbonEventHandler events,
@DataDirectory final Path dataDirectory,
final LocaleSerializerConfigurate locale,
final Logger logger
final Logger logger,
final Set<Integration.ConfigMeta> integrations
) {
this.dataDirectory = dataDirectory;
this.locale = locale;
this.logger = logger;
this.integrations = integrations;

events.subscribe(CarbonReloadEvent.class, -100, true, event -> this.reloadPrimaryConfig());
}
Expand Down Expand Up @@ -120,6 +125,7 @@ public ConfigurationLoader<?> configurationLoader(final Path file) {
.serializers(serializerBuilder ->
serializerBuilder.registerAll(serializer.serializers())
.register(Locale.class, this.locale)
.register(IntegrationConfigContainer.class, new IntegrationConfigContainer.Serializer(this.integrations))
.registerAnnotatedObjects(ObjectMapper.factoryBuilder()
.addProcessor(Comment.class, overrideComments())
.build()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* CarbonChat
*
* Copyright (c) 2023 Josua Parks (Vicarious)
* Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.draycia.carbon.common.config;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import net.draycia.carbon.common.integration.Integration;
import net.draycia.carbon.common.util.Exceptions;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.spongepowered.configurate.BasicConfigurationNode;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;

@DefaultQualifier(NonNull.class)
public final class IntegrationConfigContainer {

private final Map<String, Object> map = new HashMap<>();

@SuppressWarnings("unchecked")
public <C> C config(final Integration.ConfigMeta meta) {
return (C) Objects.requireNonNull(this.map.get(meta.name()));
}

public static final class Serializer implements TypeSerializer<IntegrationConfigContainer> {
private final Map<String, Type> sections;

public Serializer(final Set<Integration.ConfigMeta> integrations) {
this.sections = integrations.stream().collect(Collectors.toMap(Integration.ConfigMeta::name, Integration.ConfigMeta::type));
}

@Override
public IntegrationConfigContainer deserialize(final Type type, final ConfigurationNode node) throws SerializationException {
final IntegrationConfigContainer container = new IntegrationConfigContainer();
for (final Map.Entry<String, Type> entry : this.sections.entrySet()) {
final @Nullable Object value = node.node(entry.getKey()).get(entry.getValue());
Objects.requireNonNull(value);
container.map.put(entry.getKey(), value);
}
return container;
}

@Override
public void serialize(final Type type, final @Nullable IntegrationConfigContainer obj, final ConfigurationNode node) throws SerializationException {
Objects.requireNonNull(obj);

for (final Object key : node.childrenMap().keySet()) {
node.removeChild(key);
}

for (final Map.Entry<String, Type> entry : this.sections.entrySet()) {
node.node(entry.getKey()).set(entry.getValue(), obj.map.get(entry.getKey()));
}
}

@Override
public IntegrationConfigContainer emptyValue(final Type specificType, final ConfigurationOptions options) {
final IntegrationConfigContainer container = new IntegrationConfigContainer();
for (final Map.Entry<String, Type> entry : this.sections.entrySet()) {
final @Nullable Object value;
try {
value = options.serializers().get(entry.getValue())
.deserialize(entry.getValue(), BasicConfigurationNode.root());
} catch (final Exception e) {
throw Exceptions.rethrow(e);
}
Objects.requireNonNull(value);
container.map.put(entry.getKey(), value);
}
return container;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,24 @@ public class PrimaryConfig {
private NicknameSettings nicknameSettings = new NicknameSettings();
private PartySettings partyChat = new PartySettings();

@Comment("Settings for integrations with other plugins/mods")
private IntegrationConfigContainer integrations;

@Comment("Whether Carbon should check for updates using the GitHub API on startup.")
private boolean updateChecker = true;

public PartySettings partyChat() {
return this.partyChat;
}

public IntegrationConfigContainer integrations() {
return this.integrations;
}

public NicknameSettings nickname() {
return this.nicknameSettings;
}

@Comment("Whether Carbon should check for updates using the GitHub API on startup.")
private boolean updateChecker = true;

public Locale defaultLocale() {
return this.defaultLocale;
}
Expand Down