Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This guide covers the shared APIs, calling conventions, and lifecycle rules used

`ClientBootstrap` creates the components in its constructor. Each feature has a private `register...` method that creates and registers its config when needed, creates the feature, and adds it to `components`. Pass dependencies as arguments. Return the feature's API only when a later registration method needs it.

Organize the constructor into shared runtime, feature providers, other features, and platform integrations. Create runtime components and platform adapters directly in their sections. Group feature registration calls and method declarations by category in this order: Combat, Player, Render, Misc, Interface. Within each category, order methods alphabetically by name. Put features without config in the first `Category: No config` group. Use matching group and category comments in both places. Dependency order takes priority over category and name. Bootstrap starts components in list order and stops them in reverse. Registration methods only create and register objects. Register listeners and start workers in `start()`.
Organize the constructor into shared runtime, feature providers, other features, and platform integrations. Create runtime components and platform adapters directly in their sections. Group feature registration calls and method declarations by category in this order: Combat, Player, Render, Misc, Patch. Within each category, order methods alphabetically by name. Put features without a feature config, including those with only global settings, in the first `Category: No config` group. Use matching group and category comments in both places. Dependency order takes priority over category and name. Bootstrap starts components in list order and stops them in reverse. Registration methods only create and register objects. Register listeners and start workers in `start()`.

Bootstrap registers the component and calls its `start()` method. If `start()` causes an exception, then Bootstrap will stop this component and all components that were started before it. `stop()` should release resources in any case, including partial initialization and even if it was called multiple times. An exception thrown by one of the components in `stop()` will be logged but all the others will be stopped.

Expand Down
60 changes: 43 additions & 17 deletions src/main/java/pit12/bootstrap/ClientBootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.client.FMLClientHandler;
import pit12.feature.autoclick.AutoClickBinding;
import pit12.feature.autoclick.AutoClickConfig;
import pit12.feature.autoclick.AutoClickFeature;
Expand Down Expand Up @@ -66,8 +67,14 @@
import pit12.feature.relation.RelationFeature;
import pit12.feature.relation.api.RelationLookup;
import pit12.feature.relation.api.Relations;
import pit12.feature.resourceleakfix.ResourceLeakFixBinding;
import pit12.feature.resourceleakfix.ResourceLeakFixConfig;
import pit12.feature.resourceleakfix.ResourceLeakFixFeature;
import pit12.feature.ringhelper.RingHelperConfig;
import pit12.feature.ringhelper.RingHelperFeature;
import pit12.feature.scoreboardfix.ScoreboardFixBinding;
import pit12.feature.scoreboardfix.ScoreboardFixConfig;
import pit12.feature.scoreboardfix.ScoreboardFixFeature;
import pit12.feature.smartblock.SmartBlockBinding;
import pit12.feature.smartblock.SmartBlockConfig;
import pit12.feature.smartblock.SmartBlockFeature;
Expand Down Expand Up @@ -163,14 +170,14 @@ public ClientBootstrap() {
playerEquipment, relations, commands,
new File(minecraft.mcDataDir, "12pit/swap-bindings.json").toPath(),
(SwapHooksBinding) minecraft, input);
// Category: No config
BooleanSetting discordRpc =
registerWebUi(webUiConfigs, configs, profiles, relations, hudEditor, swapBindings,
new File(minecraft.mcDataDir, "12pit/webui.json").toPath());
// Category: Misc
DenickNames denickNames = registerDenick(minecraft, configs, session, pitContext, presence,
playerNames, nicks, players, playerEquipment, commands,
new File(minecraft.mcDataDir, "12pit/denick-cache.json").toPath());
// Category: Interface
BooleanSetting discordRpc =
registerWebUi(webUiConfigs, configs, profiles, relations, hudEditor, swapBindings,
new File(minecraft.mcDataDir, "12pit/webui.json").toPath());
// Features
// Category: No config
registerDiscordRpc(webUiConfigs, discordRpc);
Expand Down Expand Up @@ -199,6 +206,12 @@ public ClientBootstrap() {
// Category: Misc
registerAutoQuickMath(configs);
registerBanAlert(minecraft, configs, session, presence);
// Category: Patch
if (FMLClientHandler.instance().hasOptifine()) {
registerResourceLeakFix(configs,
(ResourceLeakFixBinding) minecraft.getTextureMapBlocks());
}
registerScoreboardFix(configs, (ScoreboardFixBinding) minecraft);
// Platform integrations
OneConfigSupport oneConfig = new OneConfigSupport(configs);
components.add(oneConfig);
Expand Down Expand Up @@ -244,6 +257,19 @@ private SwapBindings registerSwap(Minecraft minecraft, ClientThread client,
return swap.bindings();
}

// Category: No config
private BooleanSetting registerWebUi(ConfigCatalog configs, ConfigCatalog featureConfigs,
Profiles profiles, Relations relations, HudEditorFeature hudEditor,
SwapBindings swapBindings, Path path) {
WebUiConfig config = new WebUiConfig(language);
configs.register(config);
WebUiFeature webUi = new WebUiFeature(featureConfigs, configs, profiles, relations,
hudEditor, swapBindings, config, language, path);
hudEditor.setWebUiOpener(config.keybind()::get, webUi::open, config.hudEditorHint());
components.add(webUi);
return config.discordRpc();
}

// Category: Misc
private DenickNames registerDenick(Minecraft minecraft, ConfigCatalog configs,
ClientSession session, PitContext pitContext, TabPresence presence,
Expand All @@ -259,19 +285,6 @@ private DenickNames registerDenick(Minecraft minecraft, ConfigCatalog configs,
return denick;
}

// Category: Interface
private BooleanSetting registerWebUi(ConfigCatalog configs, ConfigCatalog featureConfigs,
Profiles profiles, Relations relations, HudEditorFeature hudEditor,
SwapBindings swapBindings, Path path) {
WebUiConfig config = new WebUiConfig(language);
configs.register(config);
WebUiFeature webUi = new WebUiFeature(featureConfigs, configs, profiles, relations,
hudEditor, swapBindings, config, language, path);
hudEditor.setWebUiOpener(config.keybind()::get, webUi::open, config.hudEditorHint());
components.add(webUi);
return config.discordRpc();
}

// Features
// Category: No config
private void registerDiscordRpc(ConfigCatalog configs, BooleanSetting enabled) {
Expand Down Expand Up @@ -407,6 +420,19 @@ private void registerBanAlert(Minecraft minecraft, ConfigCatalog configs, Client
.add(new BanAlertFeature(minecraft, configs, config, session, presence, language));
}

// Category: Patch
private void registerResourceLeakFix(ConfigCatalog configs, ResourceLeakFixBinding binding) {
ResourceLeakFixConfig config = new ResourceLeakFixConfig();
configs.register(config);
components.add(new ResourceLeakFixFeature(config, binding));
}

private void registerScoreboardFix(ConfigCatalog configs, ScoreboardFixBinding binding) {
ScoreboardFixConfig config = new ScoreboardFixConfig();
configs.register(config);
components.add(new ScoreboardFixFeature(config, binding));
}

public void start() {
client.check();
if (started) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public enum PlayerListGroup {
FRIEND(source("§a§lFriend")),
ENEMY(source("§c§lEnemy")),
NICK("Nick"),
NICK("§e§lNICK"),
REGULARITY("§4§lREG"),
DARK("§d§lDARK"),
BOUNTY_HUNTER("§6§lBounty Hunter");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.resourceleakfix;

public interface ResourceLeakFixBinding {
void pit12$bindResourceLeakFixConfig(ResourceLeakFixConfig config);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.resourceleakfix;

import static pit12.runtime.languages.Languages.source;

import pit12.runtime.config.ConfigCategory;
import pit12.runtime.config.FeatureConfig;

public final class ResourceLeakFixConfig extends FeatureConfig {
public ResourceLeakFixConfig() {
super("resourceleakfix", source("Resource Leak Fix"),
new ConfigCategory("patch", source("Patch")), source("for Optifine"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.resourceleakfix;

import pit12.shared.lifecycle.ClientLifecycle;

public final class ResourceLeakFixFeature implements ClientLifecycle {
private final ResourceLeakFixConfig config;
private final ResourceLeakFixBinding binding;

public ResourceLeakFixFeature(ResourceLeakFixConfig config, ResourceLeakFixBinding binding) {
this.config = config;
this.binding = binding;
}

@Override
public void start() {
binding.pit12$bindResourceLeakFixConfig(config);
}

@Override
public void stop() {
binding.pit12$bindResourceLeakFixConfig(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.scoreboardfix;

public interface ScoreboardFixBinding {
void pit12$bindScoreboardFixConfig(ScoreboardFixConfig config);

boolean pit12$scoreboardFixEnabled();
}
31 changes: 31 additions & 0 deletions src/main/java/pit12/feature/scoreboardfix/ScoreboardFixConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.scoreboardfix;

import static pit12.runtime.languages.Languages.source;

import pit12.runtime.config.ConfigCategory;
import pit12.runtime.config.FeatureConfig;

public final class ScoreboardFixConfig extends FeatureConfig {
public ScoreboardFixConfig() {
super("scoreboardfix", source("Scoreboard Fix"),
new ConfigCategory("patch", source("Patch")), "");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* This file is part of 12pit.
*
* Copyright (C) 2026 The 12pit Authors and contributors <https://github.com/12src/12pit>
*
* 12pit 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.
*
* 12pit 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 12pit. If not, see <https://www.gnu.org/licenses/>.
*/
package pit12.feature.scoreboardfix;

import pit12.shared.lifecycle.ClientLifecycle;

public final class ScoreboardFixFeature implements ClientLifecycle {
private final ScoreboardFixConfig config;
private final ScoreboardFixBinding binding;

public ScoreboardFixFeature(ScoreboardFixConfig config, ScoreboardFixBinding binding) {
this.config = config;
this.binding = binding;
}

@Override
public void start() {
binding.pit12$bindScoreboardFixConfig(config);
}

@Override
public void stop() {
binding.pit12$bindScoreboardFixConfig(null);
}
}
4 changes: 1 addition & 3 deletions src/main/java/pit12/feature/webui/WebUiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import pit12.runtime.config.BooleanSetting;
import pit12.runtime.config.ChoiceSetting;
import pit12.runtime.config.ChoiceSetting.Choice;
import pit12.runtime.config.ConfigCategory;
import pit12.runtime.config.FeatureConfig;
import pit12.runtime.config.KeybindSetting;
import pit12.runtime.languages.Languages;
Expand All @@ -38,8 +37,7 @@ public final class WebUiConfig extends FeatureConfig {
private final BooleanSetting discordRpc;

public WebUiConfig(Languages languages) {
super("webui", source("Settings"), new ConfigCategory("interface", source("Interface")), "",
false);
super("webui", source("Settings"), null, "", false);
Map<Integer, String> names = languages.names();
Choice[] choices = new Choice[names.size()];
int index = 0;
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/pit12/feature/webui/WebUiServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import pit12.feature.swap.api.SwapBindings;
import pit12.runtime.config.ChoiceSetting;
import pit12.runtime.config.ConfigCatalog;
import pit12.runtime.config.ConfigCategory;
import pit12.runtime.config.ConfigChangeListener;
import pit12.runtime.config.ConfigOption;
import pit12.runtime.config.FeatureConfig;
Expand Down Expand Up @@ -547,10 +548,11 @@ private Map<String, Object> featureState(FeatureConfig feature) {
List<Object> options = (List<Object>) section.get("options");
options.add(optionState(option));
}
ConfigCategory category = feature.category();
return object("id", feature.id(), "name", feature.displayName(), "description",
feature.description(), "categoryId", feature.category().id(), "category",
feature.category().displayName(), "toggleable", feature.toggleable(), "enabled",
feature.enabled(), "sections", sections);
feature.description(), "categoryId", category == null ? "" : category.id(),
"category", category == null ? "" : category.displayName(), "toggleable",
feature.toggleable(), "enabled", feature.enabled(), "sections", sections);
}

private static String buildLabel() {
Expand Down
Loading
Loading