Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d02bb4b0ac | ||
|
|
49e429ce1b | ||
|
|
4f95e82812 | ||
|
|
89e6b6f680 | ||
|
|
fc80845f5c | ||
|
|
8053411565 | ||
|
|
c92059db26 | ||
|
|
fdc7d867ec | ||
|
|
b8a283a177 | ||
|
|
3a2b403acc | ||
|
|
37fcdfb277 | ||
|
|
20330310fe | ||
|
|
f4372ab8a4 | ||
|
|
7a1a4306b1 | ||
|
|
058c18c4cb | ||
|
|
3f389f65f2 | ||
|
|
19f0e05c8e | ||
|
|
722802c001 | ||
|
|
4a48d2c721 | ||
|
|
159a594b2c |
+3
-8
@@ -22,16 +22,11 @@ server/crash-reports/
|
||||
server/versions/
|
||||
server/*.log
|
||||
server/*.json
|
||||
server/config/
|
||||
server/fabricloader.log
|
||||
server/fabric-server-launcher.properties
|
||||
quarantine/*.jar
|
||||
|
||||
# server runtime
|
||||
server/world/
|
||||
server/logs/
|
||||
server/crash-reports/
|
||||
server/*.log
|
||||
|
||||
# OS / IDE
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -43,10 +38,10 @@ Thumbs.db
|
||||
.env
|
||||
atlas/.env
|
||||
server/.fabric/
|
||||
server/config/**/update/
|
||||
mods/*/.gradle/
|
||||
mods/*/build/
|
||||
mods/*/logs/
|
||||
.gradle/
|
||||
mods/pala/.direnv/
|
||||
.direnv/
|
||||
mods/pala/.classpath
|
||||
mods/pala/.project
|
||||
|
||||
+175
-43
@@ -10,7 +10,12 @@
|
||||
"address": "mcpv.ihorizon.org:25565",
|
||||
"minecraftVersion": "26.3",
|
||||
"mainServer": true,
|
||||
"autoconnect": true,
|
||||
"autoconnect": false,
|
||||
"javaOptions": {
|
||||
"distribution": "TEMURIN",
|
||||
"supported": ">=25.x",
|
||||
"suggestedMajor": 25
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"id": "net.fabricmc:fabric-loader:0.20.0",
|
||||
@@ -94,6 +99,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "com.autovw:advancednetherite:2.4.4-26.3@jar",
|
||||
"name": "Advanced Netherite",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 568803,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/advancednetherite-fabric-2.4.4-26.3.jar",
|
||||
"MD5": "ed5a33f1c9d190369eaaa9a757ad0678"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "squeek.appleskin:appleskin:3.0.10+mc26.3@jar",
|
||||
"name": "AppleSkin",
|
||||
@@ -147,6 +162,26 @@
|
||||
"MD5": "522b9df994d1a6111aa3d716d364073b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "org.betterx:betternether:26.301.2@jar",
|
||||
"name": "Better Nether",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 27986160,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/better-nether-26.301.2.jar",
|
||||
"MD5": "dae1d6ca17260a928bd2a685b169adde"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "net.Pandarix:betterarcheology:26.3-1.3.9@jar",
|
||||
"name": "Better Archeology",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 3113287,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/betterarcheology-fabric-26.3-1.3.9.jar",
|
||||
"MD5": "b80841a76199f6df1facc8e6a7df7a64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "net.bettercombat:bettercombat:3.2.2@jar",
|
||||
"name": "Better Combat",
|
||||
@@ -223,6 +258,16 @@
|
||||
"MD5": "19d0bb757fbc910514254261bb022e91"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dev.smto:constructionwand:2.1.3@jar",
|
||||
"name": "Construction Wand (Fabric)",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 306717,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/constructionwand-2.1.3+26.3.jar",
|
||||
"MD5": "e7df688426cc24b62474386086be5dd7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "me.pepperbell:continuity:3.0.1+26.3@jar",
|
||||
"name": "Continuity",
|
||||
@@ -259,6 +304,16 @@
|
||||
"MD5": "b59adc60f71cb3bf517b876b64689c91"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.vsngarcia:elevatorid:26.3-1.16.6@jar",
|
||||
"name": "ElevatorMod",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 780593,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/elevatorid-fabric-26.3-1.16.6.jar",
|
||||
"MD5": "8046d4dfc93e80121d47db42292fea5b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "generated.fabricmod:fabric-api:0.163.0+26.3@jar",
|
||||
"name": "Fabric API",
|
||||
@@ -292,6 +347,16 @@
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fuzs.forgeconfigapiport.fabric.impl:forgeconfigapiport:26.3.1@jar",
|
||||
"name": "Forge Config API Port",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 609574,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ForgeConfigAPIPort-v26.3.1-mc26.3.x-Fabric.jar",
|
||||
"MD5": "a16f7b8b0f57d3f2fb2fb9d4adab070c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "glitchcore.fabric.core:glitchcore:26.3.0.0.5@jar",
|
||||
"name": "GlitchCore",
|
||||
@@ -303,13 +368,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.blocklegend001:immersiveores:26.3-0.1@jar",
|
||||
"name": "ImmersiveOres",
|
||||
"id": "generated.fabricmod:ipo:7@jar",
|
||||
"name": "ImprovedPillagerOutpost",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 543252,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImmersiveOres-26.3-0.1.jar",
|
||||
"MD5": "840a11c59c29eadd909cba3ce6f569bb"
|
||||
"size": 614666,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImprovedPillagerOutpost-v7.jar",
|
||||
"MD5": "5806e860495c8a4a7294a5249b41f968"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -391,6 +456,16 @@
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "net.trique:mythicupgrades:5.1.1@jar",
|
||||
"name": "Mythic Upgrades",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 2375834,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/mythicupgrades-fabric-26.3-5.1.1.jar",
|
||||
"MD5": "09e9e8eaf95febf0835780c073220f28"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.chaosthedude:naturescompass:26.3-2.5.1-fabric@jar",
|
||||
"name": "Nature's Compass",
|
||||
@@ -402,13 +477,23 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fr.surviepote:nosingleplayer:1.1.2@jar",
|
||||
"id": "com.aizistral:nochatreports:26.3-v2.21.0@jar",
|
||||
"name": "No Chat Reports",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 239561,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/NoChatReports-FABRIC-26.3-v2.21.0.jar",
|
||||
"MD5": "8434b3b01bbad6e6deb5309d3e2c5800"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fr.surviepote:nosingleplayer:1.1.8@jar",
|
||||
"name": "NoSingleplayer (SurviePote)",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 2292471,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.2.jar",
|
||||
"MD5": "2ba50ec99adb670cdb34fe41b0e3d7dd"
|
||||
"size": 3637392,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.8.jar",
|
||||
"MD5": "dc69cd7ebc5d10ba4797c4e212e47e0c"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -461,6 +546,26 @@
|
||||
"MD5": "e176ae70db08a26839282e6d373b5d97"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fuzs.puzzleslib.fabric.impl:puzzleslib:26.3.15@jar",
|
||||
"name": "Puzzles Lib",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 1093312,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/puzzleslib-v26.3.15-mc26.3.x+fabric.jar",
|
||||
"MD5": "51ceba62abb73e69212caa0f923eacff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.teamresourceful.resourcefulconfig.web.server:resourcefulconfig:6.0.2@jar",
|
||||
"name": "Resourcefulconfig",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 444092,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ResourcefulConfig-6.0.2.jar",
|
||||
"MD5": "f9817fe0bf73467af9c00eeaee420550"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "net.blay09.mods:shogi:26.3.0.3@jar",
|
||||
"name": "Shogi",
|
||||
@@ -471,6 +576,16 @@
|
||||
"MD5": "7d4488104a6d0714838b5c801f50407d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "zy.swthb:simple-warp-tpa-home-back:26.3-1.3@jar",
|
||||
"name": "Simple-warp-tpa-home-back",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 67653,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/simple-warp-tpa-home-back-26.3-1.3.jar",
|
||||
"MD5": "8e6235409b851c40691f65d729130e82"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dev.tr7zw.skinlayers:skinlayers3d:1.11.3@jar",
|
||||
"name": "3d-Skin-Layers",
|
||||
@@ -497,6 +612,16 @@
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "generated.fabricmod:spatial:gui-1.8+26.3-fabric@jar",
|
||||
"name": "Spatial",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 276562,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/spatial-gui-1.8+26.3-fabric.jar",
|
||||
"MD5": "80fcb9b1ed083b8515fa78e6a5cd86a4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.jaquadro.minecraft:storagedrawers:26.3.0.1@jar",
|
||||
"name": "Storage Drawers",
|
||||
@@ -517,6 +642,16 @@
|
||||
"MD5": "310a79696ae06b2049c29f1689c86db1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.tiviacz:travelersbackpack:11.4.1@jar",
|
||||
"name": "Traveler's Backpack",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 1511186,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/travelersbackpack-fabric-26.3-11.4.1.jar",
|
||||
"MD5": "a18139ae9428e3072db547749642cd5d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.serilum:treeharvester:10.3@jar",
|
||||
"name": "Tree Harvester",
|
||||
@@ -527,6 +662,16 @@
|
||||
"MD5": "63fe436df405aa30d6adf50ebf65737f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fuzs.universalenchants:universalenchants:26.3.1@jar",
|
||||
"name": "Universal Enchants",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 198420,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/universalenchants-v26.3.1-mc26.3.x+fabric.jar",
|
||||
"MD5": "d8ed00b6e2d334da7e469686f6b64b59"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "net.execheinz:upgrader:1.2.1@jar",
|
||||
"name": "Upgrader",
|
||||
@@ -619,6 +764,26 @@
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dev.isxander.yacl3.platform:yet_another_config_lib_v3:3.9.7+26.3-fabric@jar",
|
||||
"name": "YetAnotherConfigLib",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 1125377,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/yet_another_config_lib_v3-3.9.7+26.3-fabric.jar",
|
||||
"MD5": "967207b5cf360d9481fb318abacfe8eb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.yyz:yyzsbackpack:0.0.11-reborn+26.3@jar",
|
||||
"name": "Yyz's Backpack",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 357667,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/yyzsbackpack-0.0.11-reborn+26.3.jar",
|
||||
"MD5": "67db85ac45cf311d9994349f51d00942"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings.json",
|
||||
"name": "settings.json",
|
||||
@@ -773,39 +938,6 @@
|
||||
"path": "config/easy_homes.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "enderium-common.toml",
|
||||
"name": "enderium-common.toml",
|
||||
"type": "File",
|
||||
"artifact": {
|
||||
"size": 3930,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/enderium-common.toml",
|
||||
"MD5": "29c0e8dc797b264ff04944f44756ff16",
|
||||
"path": "config/immersiveores/enderium-common.toml"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vibranium-common.toml",
|
||||
"name": "vibranium-common.toml",
|
||||
"type": "File",
|
||||
"artifact": {
|
||||
"size": 3531,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/vibranium-common.toml",
|
||||
"MD5": "425488f8ca763dc9c5f1c406f79a7f53",
|
||||
"path": "config/immersiveores/vibranium-common.toml"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "vulpus-common.toml",
|
||||
"name": "vulpus-common.toml",
|
||||
"type": "File",
|
||||
"artifact": {
|
||||
"size": 3466,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/vulpus-common.toml",
|
||||
"MD5": "05cc5da3fcd2dab7314b62636043342f",
|
||||
"path": "config/immersiveores/vulpus-common.toml"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "jade.json",
|
||||
"name": "jade.json",
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,68 +0,0 @@
|
||||
|
||||
# --- Enderium Armor Protection and Resistance ---
|
||||
protectionValueEnderiumBoots=12 #Protection value of Enderium Boots | default: 12
|
||||
protectionValueEnderiumLeggings=16 #Protection value of Enderium Leggings | default: 16
|
||||
protectionValueEnderiumChestplate=20 #Protection value of Enderium Chestplate | default: 20
|
||||
protectionValueEnderiumHelmet=12 #Protection value of Enderium Helmet | default: 12
|
||||
toughnessValueEnderiumArmor=10 #Toughness value of Enderium Armor | default: 10
|
||||
knockbackResistanceValueEnderiumArmor=0.4 #Knockback resistance of Enderium Armor | default: 0.4
|
||||
enchantmentValueEnderiumArmor=65 #Enchantment value of Enderium Armor | default: 65
|
||||
|
||||
# --- Enderium Armor Special Effects and Abilities ---
|
||||
speedIIIEnderiumArmor=true #Speed III effect for Enderium Armor | default: true
|
||||
jumpIIIEnderiumArmor=true #Jump III effect for Enderium Armor | default: true
|
||||
nightVisionEnderiumArmor=true #Night Vision effect for Enderium Armor | default: true
|
||||
immuneToFallDamageEnderiumArmor=true #Immunity to fall damage for Enderium Armor | default: true
|
||||
fireResistanceEnderiumArmor=true #Fire resistance for Enderium Armor | default: true
|
||||
neverLoseHungerEnderiumArmor=true #Never lose hunger with Enderium Armor | default: true
|
||||
canFlyEnderiumArmor=true #Ability to fly with Enderium Armor | default: true
|
||||
canWalkOnPowderedSnowEnderium=true #Can walk on powdered snow with Enderium Armor | default: true
|
||||
makesPiglinsNeutralEnderium=true #Makes Piglins neutral with Enderium Armor | default: true
|
||||
endermanWillNotBeAngryWithYouEnderium=true #Endermen won't be angry with you with Enderium Armor | default: true
|
||||
|
||||
# --- Enderium Bow Settings ---
|
||||
enchantmentValueEnderiumBow=5 #Enchantment value of Enderium Bow | default: 5
|
||||
damageEnderiumBow=14 #Damage value of Enderium Bow | default: 14
|
||||
arrowCountEnderiumBow=4 #Arrow count for Enderium Bow | default: 4
|
||||
|
||||
# --- Enderium Tool Tier Base Properties ---
|
||||
speedEnderiumTier=50 #Speed value of Enderium tier | default: 50
|
||||
enchantmentValueEnderiumTier=65 #Enchantment value of Enderium tier | default: 65
|
||||
attackDamageBonusEnderiumTier=3 #Attack damage bonus of Enderium tier | default: 3
|
||||
durabilityEnderium=5139 #Durability of Enderium tools | default: 5139
|
||||
unbreakableEnderium=false #Whether Enderium tools are unbreakable | default: false
|
||||
|
||||
# --- Enderium Pickaxe Stats ---
|
||||
attackDamageEnderiumPickaxe=15 #Attack damage of Enderium Pickaxe | default: 15
|
||||
attackSpeedEnderiumPickaxe=1.0 #Attack speed of Enderium Pickaxe | default: 1.0
|
||||
|
||||
# --- Enderium Axe Stats ---
|
||||
attackDamageEnderiumAxe=18 #Attack damage of Enderium Axe | default: 18
|
||||
attackSpeedEnderiumAxe=1.0 #Attack speed of Enderium Axe | default: 1.0
|
||||
|
||||
# --- Enderium Shovel Stats ---
|
||||
attackDamageEnderiumShovel=15 #Attack damage of Enderium Shovel | default: 15
|
||||
attackSpeedEnderiumShovel=1.0 #Attack speed of Enderium Shovel | default: 1.0
|
||||
|
||||
# --- Enderium Sword Stats ---
|
||||
attackDamageEnderiumSword=22 #Attack damage of Enderium Sword | default: 22
|
||||
attackSpeedEnderiumSword=1.0 #Attack speed of Enderium Sword | default: 1.0
|
||||
|
||||
# --- Enderium Hoe Stats ---
|
||||
attackDamageEnderiumHoe=15 #Attack damage of Enderium Hoe | default: 15
|
||||
attackSpeedEnderiumHoe=1.0 #Attack speed of Enderium Hoe | default: 1.0
|
||||
|
||||
# --- Enderium Paxel Stats ---
|
||||
attackDamageEnderiumPaxel=18 #Attack damage of Enderium Paxel | default: 18
|
||||
attackSpeedEnderiumPaxel=1.0 #Attack speed of Enderium Paxel | default: 1.0
|
||||
|
||||
# --- Enderium Hammer Stats ---
|
||||
attackDamageEnderiumHammer=16 #Attack damage of Enderium Hammer | default: 16
|
||||
attackSpeedEnderiumHammer=1.0 #Attack speed of Enderium Hammer | default: 1.0
|
||||
radiusEnderiumHammer=3 #Hammering radius for Enderium Hammer | default: 3
|
||||
|
||||
# --- Enderium Excavator Stats ---
|
||||
attackDamageEnderiumExcavator=16 #Attack damage of Enderium Excavator | default: 16
|
||||
attackSpeedEnderiumExcavator=1.0 #Attack speed of Enderium Excavator | default: 1.0
|
||||
radiusEnderiumExcavator=3 #Excavation radius for Enderium Excavator | default: 3
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
# --- --- Vibranium Armor Protection and Resistance --- ---
|
||||
protectionValueVibraniumBoots=6 #Protection value of Vibranium Boots | default: 6
|
||||
protectionValueVibraniumLeggings=9 #Protection value of Vibranium Leggings | default: 9
|
||||
protectionValueVibraniumChestplate=12 #Protection value of Vibranium Chestplate | default: 12
|
||||
protectionValueVibraniumHelmet=6 #Protection value of Vibranium Helmet | default: 6
|
||||
toughnessValueVibraniumArmor=3 #Toughness value of Vibranium Armor | default: 3
|
||||
knockbackResistanceValueVibraniumArmor=0.2 #Knockback resistance of Vibranium Armor | default: 0.2
|
||||
enchantmentValueVibraniumArmor=55 #Enchantment value of Vibranium Armor | default: 55
|
||||
|
||||
# --- --- Vibranium Armor Special Effects and Abilities --- ---
|
||||
speedIVibraniumArmor=true #Speed I effect for Vibranium Armor | default: true
|
||||
jumpIVibraniumArmor=true #Jump I effect for Vibranium Armor | default: true
|
||||
nightVisionVibraniumArmor=true #Night Vision effect for Vibranium Armor | default: true
|
||||
canWalkOnPowderedSnowVibranium=true #Can walk on powdered snow with Vibranium Armor | default: true
|
||||
|
||||
# --- --- Vibranium Bow Settings --- ---
|
||||
enchantmentValueVibraniumBow=5 #Enchantment value of Vibranium Bow | default: 5
|
||||
damageVibraniumBow=5 #Damage value of Vibranium Bow | default: 5
|
||||
arrowCountVibraniumBow=2 #Arrow count for Vibranium Bow | default: 2
|
||||
|
||||
# --- --- Vibranium Tool Tier Base Properties --- ---
|
||||
speedVibraniumTier=30 #Speed value of Vibranium tier | default: 30
|
||||
enchantmentValueVibraniumTier=55 #Enchantment value of Vibranium tier | default: 55
|
||||
attackDamageBonusVibraniumTier=1 #Attack damage bonus of Vibranium tier | default: 1
|
||||
durabilityVibranium=2562 #Durability of Vibranium tools | default: 2562
|
||||
unbreakableVibranium=false #Whether Vibranium tools are unbreakable | default: false
|
||||
|
||||
# --- --- Vibranium Pickaxe Stats --- ---
|
||||
attackDamageVibraniumPickaxe=7 #Attack damage of Vibranium Pickaxe | default: 7
|
||||
attackSpeedVibraniumPickaxe=0.5 #Attack speed of Vibranium Pickaxe | default: 0.5
|
||||
|
||||
# --- --- Vibranium Axe Stats --- ---
|
||||
attackDamageVibraniumAxe=12 #Attack damage of Vibranium Axe | default: 12
|
||||
attackSpeedVibraniumAxe=0.5 #Attack speed of Vibranium Axe | default: 0.5
|
||||
|
||||
# --- --- Vibranium Shovel Stats --- ---
|
||||
attackDamageVibraniumShovel=7 #Attack damage of Vibranium Shovel | default: 7
|
||||
attackSpeedVibraniumShovel=0.5 #Attack speed of Vibranium Shovel | default: 0.5
|
||||
|
||||
# --- --- Vibranium Sword Stats --- ---
|
||||
attackDamageVibraniumSword=10 #Attack damage of Vibranium Sword | default: 10
|
||||
attackSpeedVibraniumSword=0.5 #Attack speed of Vibranium Sword | default: 0.5
|
||||
|
||||
# --- --- Vibranium Hoe Stats --- ---
|
||||
attackDamageVibraniumHoe=7 #Attack damage of Vibranium Hoe | default: 7
|
||||
attackSpeedVibraniumHoe=0.5 #Attack speed of Vibranium Hoe | default: 0.5
|
||||
|
||||
# --- --- Vibranium Paxel Stats --- ---
|
||||
attackDamageVibraniumPaxel=11 #Attack damage of Vibranium Paxel | default: 11
|
||||
attackSpeedVibraniumPaxel=0.5 #Attack speed of Vibranium Paxel | default: 0.5
|
||||
|
||||
# --- --- Vibranium Hammer Stats --- ---
|
||||
attackDamageVibraniumHammer=7 #Attack damage of Vibranium Hammer | default: 7
|
||||
attackSpeedVibraniumHammer=0.5 #Attack speed of Vibranium Hammer | default: 0.5
|
||||
radiusVibraniumHammer=1 #Hammering radius for Vibranium Hammer | default: 1
|
||||
|
||||
# --- --- Vibranium Excavator Stats --- ---
|
||||
attackDamageVibraniumExcavator=7 #Attack damage of Vibranium Excavator | default: 7
|
||||
attackSpeedVibraniumExcavator=0.5 #Attack speed of Vibranium Excavator | default: 0.5
|
||||
radiusVibraniumExcavator=1 #Excavation radius for Vibranium Excavator | default: 1
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
# --- Vulpus Armor Protection and Resistance ---
|
||||
protectionValueVulpusBoots=9 #Protection value of Vulpus Boots | default: 9
|
||||
protectionValueVulpusLeggings=12 #Protection value of Vulpus Leggings | default: 12
|
||||
protectionValueVulpusChestplate=16 #Protection value of Vulpus Chestplate | default: 16
|
||||
protectionValueVulpusHelmet=9 #Protection value of Vulpus Helmet | default: 9
|
||||
toughnessValueVulpusArmor=7 #Toughness value of Vulpus Armor | default: 7
|
||||
enchantmentValueVulpusArmor=65 #Enchantment value of Vulpus Armor | default: 65
|
||||
knockbackResistanceValueVulpusArmor=0.3 #Knockback resistance of Vulpus Armor | default: 0.3
|
||||
|
||||
# --- Vulpus Armor Special Effects and Abilities ---
|
||||
speedIIVulpusArmor=true #Speed II effect for Vulpus Armor | default: true
|
||||
jumpIIVulpusArmor=true #Jump II effect for Vulpus Armor | default: true
|
||||
nightVisionVulpusArmor=true #Night Vision for Vulpus Armor | default: true
|
||||
fireResistanceVulpusArmor=true #Fire Resistance for Vulpus Armor | default: true
|
||||
immuneToFallDamageVulpusArmor=true #Fall Damage Immunity for Vulpus Armor | default: true
|
||||
neverLoseHungerVulpusArmor=true #Prevention of Hunger Loss for Vulpus Armor | default: true
|
||||
canWalkOnPowderedSnowVulpus=true #Walking on Powdered Snow with Vulpus Armor | default: true
|
||||
makesPiglinsNeutralVulpus=true #Piglins become Neutral with Vulpus Armor | default: true
|
||||
|
||||
# --- Vulpus Bow ---
|
||||
enchantmentValueVulpusBow=5 #Enchantment value of Vulpus Bow | default: 5
|
||||
damageVulpusBow=10 #Damage of Vulpus Bow | default: 10
|
||||
arrowCountVulpusBow=3 #Arrow count for Vulpus Bow | default: 3
|
||||
|
||||
# --- Vulpus Tool Tier Base ---
|
||||
speedVulpusTier=40 #Mining speed of Vulpus tier | default: 40
|
||||
enchantmentValueVulpusTier=65 #Enchantment value of Vulpus tier | default: 65
|
||||
attackDamageBonusVulpusTier=3 #Attack damage bonus of Vulpus tier | default: 3
|
||||
durabilityVulpus=3093 #Durability of Vulpus tools | default: 3093
|
||||
unbreakableVulpus=false #Whether Vulpus tools are unbreakable | default: false
|
||||
|
||||
# --- Vulpus Pickaxe ---
|
||||
attackDamageVulpusPickaxe=10 #Attack damage of Vulpus Pickaxe | default: 10
|
||||
attackSpeedVulpusPickaxe=0.5 #Attack speed of Vulpus Pickaxe | default: 0.5
|
||||
|
||||
# --- Vulpus Axe ---
|
||||
attackDamageVulpusAxe=15 #Attack damage of Vulpus Axe | default: 15
|
||||
attackSpeedVulpusAxe=0.5 #Attack speed of Vulpus Axe | default: 0.5
|
||||
|
||||
# --- Vulpus Shovel ---
|
||||
attackDamageVulpusShovel=10 #Attack damage of Vulpus Shovel | default: 10
|
||||
attackSpeedVulpusShovel=0.5 #Attack speed of Vulpus Shovel | default: 0.5
|
||||
|
||||
# --- Vulpus Sword ---
|
||||
attackDamageVulpusSword=14 #Attack damage of Vulpus Sword | default: 14
|
||||
attackSpeedVulpusSword=0.5 #Attack speed of Vulpus Sword | default: 0.5
|
||||
|
||||
# --- Vulpus Hoe ---
|
||||
attackDamageVulpusHoe=10 #Attack damage of Vulpus Hoe | default: 10
|
||||
attackSpeedVulpusHoe=0.5 #Attack speed of Vulpus Hoe | default: 0.5
|
||||
|
||||
# --- Vulpus Paxel ---
|
||||
attackDamageVulpusPaxel=15 #Attack damage of Vulpus Paxel | default: 15
|
||||
attackSpeedVulpusPaxel=0.5 #Attack speed of Vulpus Paxel | default: 0.5
|
||||
|
||||
# --- Vulpus Hammer ---
|
||||
attackDamageVulpusHammer=11 #Attack damage of Vulpus Hammer | default: 11
|
||||
attackSpeedVulpusHammer=0.5 #Attack speed of Vulpus Hammer | default: 0.5
|
||||
radiusVulpusHammer=2 #Hammering radius for Vulpus Hammer | default: 2
|
||||
|
||||
# --- Vulpus Excavator ---
|
||||
attackDamageVulpusExcavator=11 #Attack damage of Vulpus Excavator | default: 11
|
||||
attackSpeedVulpusExcavator=0.5 #Attack speed of Vulpus Excavator | default: 0.5
|
||||
radiusVulpusExcavator=2 #Excavation radius for Vulpus Excavator | default: 2
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
"icon": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/SurviePote-26.3.png",
|
||||
"address": "mcpv.ihorizon.org:25565",
|
||||
"mainServer": true,
|
||||
"autoconnect": true
|
||||
"autoconnect": false,
|
||||
"javaOptions": {
|
||||
"distribution": "TEMURIN",
|
||||
"supported": ">=25.x",
|
||||
"suggestedMajor": 25
|
||||
}
|
||||
},
|
||||
"fabric": {
|
||||
"version": "0.20.0"
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<%- include('frame') %>
|
||||
<div id="main">
|
||||
<%- include('welcome') %>
|
||||
<%- include('login') %>
|
||||
<%- include('waiting') %>
|
||||
<%- include('loginOptions') %>
|
||||
<%- include('settings') %>
|
||||
|
||||
@@ -3208,109 +3208,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* Wrapper container for the mojang status bar. */
|
||||
#mojangStatusWrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Icon which displays the status of the mojang services. */
|
||||
#mojang_status_icon {
|
||||
font-size: 30px;
|
||||
color: #848484;
|
||||
margin-left: 15px;
|
||||
font-family: 'sans-serif';
|
||||
}
|
||||
|
||||
/* Tooltip which displays more details about the mojang statuses. */
|
||||
#mojangStatusTooltip {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
width: 145px;
|
||||
min-height: 150px;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
z-index: 1;
|
||||
font-family: 'Avenir Medium';
|
||||
font-size: 12px;
|
||||
transition: visibility 0s linear 0.25s, opacity 0.25s ease;
|
||||
bottom: calc(100% + 15px);
|
||||
transform: translateX(-50%);
|
||||
margin-left: 50%;
|
||||
box-shadow: 0px 0px 20px rgb(0, 0, 0);
|
||||
cursor: default;
|
||||
}
|
||||
#mojangStatusTooltip:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: rgba(0, 0, 0, 0.75) transparent transparent transparent;
|
||||
}
|
||||
#mojangStatusWrapper:hover #mojangStatusTooltip {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
/* Tooltip title for the mojang statuses. */
|
||||
#mojangStatusTooltipTitle {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* Wrapper container for the non essential services title. */
|
||||
#mojangStatusNEContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
/* White bar which surrounds the non essential service title. */
|
||||
.mojangStatusNEBar {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Non essential service title text. */
|
||||
#mojangStatusNETitle {
|
||||
font-size: 10px;
|
||||
padding: 0px 3px;
|
||||
text-align: center;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* Wrapper container for mojang service information. */
|
||||
.mojangStatusContainer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Displays the name of the mojang service. */
|
||||
.mojangStatusName {
|
||||
width: 100%;
|
||||
font-size: 10px;
|
||||
letter-spacing: 1px;
|
||||
line-height: 12px;
|
||||
padding: 6px 0px;
|
||||
}
|
||||
|
||||
/* Displays the status of the mojang service. */
|
||||
.mojangStatusIcon {
|
||||
margin-right: 10px;
|
||||
font-size: 18.5px;
|
||||
color: #848484;
|
||||
}
|
||||
|
||||
/* * *
|
||||
* Landing View (Bottom Styles) | Center Content
|
||||
* * */
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* AuthManager
|
||||
*
|
||||
* This module aims to abstract login procedures. Results from Mojang's REST api
|
||||
* are retrieved through our Mojang module. These results are processed and stored,
|
||||
* if applicable, in the config using the ConfigManager. All login procedures should
|
||||
* be made through this module.
|
||||
* This module aims to abstract login procedures. Microsoft auth results
|
||||
* are retrieved through our Microsoft module. These results are processed and
|
||||
* stored, if applicable, in the config using the ConfigManager. All login
|
||||
* procedures should be made through this module.
|
||||
*
|
||||
* @module authmanager
|
||||
*/
|
||||
@@ -12,7 +12,6 @@
|
||||
const ConfigManager = require('./configmanager')
|
||||
const { LoggerUtil } = require('helios-core')
|
||||
const { RestResponseStatus } = require('helios-core/common')
|
||||
const { MojangRestAPI, MojangErrorCode } = require('helios-core/mojang')
|
||||
const { MicrosoftAuth, MicrosoftErrorCode } = require('helios-core/microsoft')
|
||||
const { AZURE_CLIENT_ID } = require('./ipcconstants')
|
||||
const Lang = require('./langloader')
|
||||
@@ -51,122 +50,8 @@ function microsoftErrorDisplayable(errorCode) {
|
||||
}
|
||||
}
|
||||
|
||||
function mojangErrorDisplayable(errorCode) {
|
||||
switch(errorCode) {
|
||||
case MojangErrorCode.ERROR_METHOD_NOT_ALLOWED:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.methodNotAllowedTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.methodNotAllowedDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_NOT_FOUND:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.notFoundTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.notFoundDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_USER_MIGRATED:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.accountMigratedTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.accountMigratedDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_INVALID_CREDENTIALS:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.invalidCredentialsTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.invalidCredentialsDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_RATELIMIT:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.tooManyAttemptsTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.tooManyAttemptsDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_INVALID_TOKEN:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.invalidTokenTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.invalidTokenDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_ACCESS_TOKEN_HAS_PROFILE:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.tokenHasProfileTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.tokenHasProfileDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_CREDENTIALS_MISSING:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.credentialsMissingTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.credentialsMissingDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_INVALID_SALT_VERSION:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.invalidSaltVersionTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.invalidSaltVersionDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_UNSUPPORTED_MEDIA_TYPE:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.unsupportedMediaTypeTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.unsupportedMediaTypeDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_GONE:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.accountGoneTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.accountGoneDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_UNREACHABLE:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.unreachableTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.unreachableDesc')
|
||||
}
|
||||
case MojangErrorCode.ERROR_NOT_PAID:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.gameNotPurchasedTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.gameNotPurchasedDesc')
|
||||
}
|
||||
case MojangErrorCode.UNKNOWN:
|
||||
return {
|
||||
title: Lang.queryJS('auth.mojang.error.unknownErrorTitle'),
|
||||
desc: Lang.queryJS('auth.mojang.error.unknownErrorDesc')
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown error code: ${errorCode}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Functions
|
||||
|
||||
/**
|
||||
* Add a Mojang account. This will authenticate the given credentials with Mojang's
|
||||
* authserver. The resultant data will be stored as an auth account in the
|
||||
* configuration database.
|
||||
*
|
||||
* @param {string} username The account username (email if migrated).
|
||||
* @param {string} password The account password.
|
||||
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
|
||||
*/
|
||||
exports.addMojangAccount = async function(username, password) {
|
||||
try {
|
||||
const response = await MojangRestAPI.authenticate(username, password, ConfigManager.getClientToken())
|
||||
console.log(response)
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
|
||||
const session = response.data
|
||||
if(session.selectedProfile != null){
|
||||
const ret = ConfigManager.addMojangAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
|
||||
if(ConfigManager.getClientToken() == null){
|
||||
ConfigManager.setClientToken(session.clientToken)
|
||||
}
|
||||
ConfigManager.save()
|
||||
return ret
|
||||
} else {
|
||||
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.ERROR_NOT_PAID))
|
||||
}
|
||||
|
||||
} else {
|
||||
return Promise.reject(mojangErrorDisplayable(response.mojangErrorCode))
|
||||
}
|
||||
|
||||
} catch (err){
|
||||
log.error(err)
|
||||
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.UNKNOWN))
|
||||
}
|
||||
}
|
||||
|
||||
const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 }
|
||||
|
||||
/**
|
||||
@@ -239,7 +124,7 @@ function calculateExpiryDate(nowMs, epiresInS) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Microsoft account. This will pass the provided auth code to Mojang's OAuth2.0 flow.
|
||||
* Add a Microsoft account. This will pass the provided auth code to Microsoft's OAuth2.0 flow.
|
||||
* The resultant data will be stored as an auth account in the configuration database.
|
||||
*
|
||||
* @param {string} authCode The authCode obtained from microsoft.
|
||||
@@ -266,31 +151,6 @@ exports.addMicrosoftAccount = async function(authCode) {
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Mojang account. This will invalidate the access token associated
|
||||
* with the account and then remove it from the database.
|
||||
*
|
||||
* @param {string} uuid The UUID of the account to be removed.
|
||||
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
|
||||
*/
|
||||
exports.removeMojangAccount = async function(uuid){
|
||||
try {
|
||||
const authAcc = ConfigManager.getAuthAccount(uuid)
|
||||
const response = await MojangRestAPI.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
ConfigManager.removeAuthAccount(uuid)
|
||||
ConfigManager.save()
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
log.error('Error while removing account', response.error)
|
||||
return Promise.reject(response.error)
|
||||
}
|
||||
} catch (err){
|
||||
log.error('Error while removing account', err)
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout
|
||||
* through the ipc renderer.
|
||||
@@ -309,41 +169,6 @@ exports.removeMicrosoftAccount = async function(uuid){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the selected account with Mojang's authserver. If the account is not valid,
|
||||
* we will attempt to refresh the access token and update that value. If that fails, a
|
||||
* new login will be required.
|
||||
*
|
||||
* @returns {Promise.<boolean>} Promise which resolves to true if the access token is valid,
|
||||
* otherwise false.
|
||||
*/
|
||||
async function validateSelectedMojangAccount(){
|
||||
const current = ConfigManager.getSelectedAccount()
|
||||
const response = await MojangRestAPI.validate(current.accessToken, ConfigManager.getClientToken())
|
||||
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
const isValid = response.data
|
||||
if(!isValid){
|
||||
const refreshResponse = await MojangRestAPI.refresh(current.accessToken, ConfigManager.getClientToken())
|
||||
if(refreshResponse.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
const session = refreshResponse.data
|
||||
ConfigManager.updateMojangAuthAccount(current.uuid, session.accessToken)
|
||||
ConfigManager.save()
|
||||
} else {
|
||||
log.error('Error while validating selected profile:', refreshResponse.error)
|
||||
log.info('Account access token is invalid.')
|
||||
return false
|
||||
}
|
||||
log.info('Account access token validated.')
|
||||
return true
|
||||
} else {
|
||||
log.info('Account access token validated.')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the selected account with Microsoft's authserver. If the account is not valid,
|
||||
* we will attempt to refresh the access token and update that value. If that fails, a
|
||||
@@ -416,10 +241,13 @@ async function validateSelectedMicrosoftAccount(){
|
||||
exports.validateSelected = async function(){
|
||||
const current = ConfigManager.getSelectedAccount()
|
||||
|
||||
if(current.type === 'microsoft') {
|
||||
return await validateSelectedMicrosoftAccount()
|
||||
} else {
|
||||
return await validateSelectedMojangAccount()
|
||||
// Auth Mojang supprimee (depreciee) : plus de sign-in Mojang au bootstrap.
|
||||
// Seuls les comptes Microsoft sont valides ; un ancien compte Mojang est
|
||||
// considere invalide sans appel reseau (l'authserver Mojang est ferme).
|
||||
if(current.type !== 'microsoft') {
|
||||
return false
|
||||
}
|
||||
|
||||
return await validateSelectedMicrosoftAccount()
|
||||
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const DEFAULT_CONFIG = {
|
||||
resWidth: 1280,
|
||||
resHeight: 720,
|
||||
fullscreen: false,
|
||||
autoConnect: true,
|
||||
autoConnect: false,
|
||||
launchDetached: true
|
||||
},
|
||||
launcher: {
|
||||
@@ -94,7 +94,6 @@ const DEFAULT_CONFIG = {
|
||||
content: null,
|
||||
dismissed: false
|
||||
},
|
||||
clientToken: null,
|
||||
selectedServer: null, // Resolved
|
||||
selectedAccount: null,
|
||||
authenticationDatabase: {},
|
||||
@@ -255,25 +254,6 @@ exports.getInstanceDirectory = function(){
|
||||
return path.join(exports.getDataDirectory(), 'instances')
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the launcher's Client Token.
|
||||
* There is no default client token.
|
||||
*
|
||||
* @returns {string} The launcher's Client Token.
|
||||
*/
|
||||
exports.getClientToken = function(){
|
||||
return config.clientToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the launcher's Client Token.
|
||||
*
|
||||
* @param {string} clientToken The launcher's new Client Token.
|
||||
*/
|
||||
exports.setClientToken = function(clientToken){
|
||||
config.clientToken = clientToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ID of the selected serverpack.
|
||||
*
|
||||
@@ -281,7 +261,7 @@ exports.setClientToken = function(clientToken){
|
||||
* @returns {string} The ID of the selected serverpack.
|
||||
*/
|
||||
exports.getSelectedServer = function(def = false){
|
||||
return !def ? config.selectedServer : DEFAULT_CONFIG.clientToken
|
||||
return !def ? config.selectedServer : DEFAULT_CONFIG.selectedServer
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,42 +293,6 @@ exports.getAuthAccount = function(uuid){
|
||||
return config.authenticationDatabase[uuid]
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the access token of an authenticated mojang account.
|
||||
*
|
||||
* @param {string} uuid The uuid of the authenticated account.
|
||||
* @param {string} accessToken The new Access Token.
|
||||
*
|
||||
* @returns {Object} The authenticated account object created by this action.
|
||||
*/
|
||||
exports.updateMojangAuthAccount = function(uuid, accessToken){
|
||||
config.authenticationDatabase[uuid].accessToken = accessToken
|
||||
config.authenticationDatabase[uuid].type = 'mojang' // For gradual conversion.
|
||||
return config.authenticationDatabase[uuid]
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an authenticated mojang account to the database to be stored.
|
||||
*
|
||||
* @param {string} uuid The uuid of the authenticated account.
|
||||
* @param {string} accessToken The accessToken of the authenticated account.
|
||||
* @param {string} username The username (usually email) of the authenticated account.
|
||||
* @param {string} displayName The in game name of the authenticated account.
|
||||
*
|
||||
* @returns {Object} The authenticated account object created by this action.
|
||||
*/
|
||||
exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName){
|
||||
config.selectedAccount = uuid
|
||||
config.authenticationDatabase[uuid] = {
|
||||
type: 'mojang',
|
||||
accessToken,
|
||||
username: username.trim(),
|
||||
uuid: uuid.trim(),
|
||||
displayName: displayName.trim()
|
||||
}
|
||||
return config.authenticationDatabase[uuid]
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tokens of an authenticated microsoft account.
|
||||
*
|
||||
@@ -357,7 +301,7 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
|
||||
* @param {string} msAccessToken The new Microsoft Access Token
|
||||
* @param {string} msRefreshToken The new Microsoft Refresh Token
|
||||
* @param {date} msExpires The date when the microsoft access token expires
|
||||
* @param {date} mcExpires The date when the mojang access token expires
|
||||
* @param {date} mcExpires The date when the Minecraft access token expires
|
||||
*
|
||||
* @returns {Object} The authenticated account object created by this action.
|
||||
*/
|
||||
@@ -376,7 +320,7 @@ exports.updateMicrosoftAuthAccount = function(uuid, accessToken, msAccessToken,
|
||||
* @param {string} uuid The uuid of the authenticated account.
|
||||
* @param {string} accessToken The accessToken of the authenticated account.
|
||||
* @param {string} name The in game name of the authenticated account.
|
||||
* @param {date} mcExpires The date when the mojang access token expires
|
||||
* @param {date} mcExpires The date when the Minecraft access token expires
|
||||
* @param {string} msAccessToken The microsoft access token
|
||||
* @param {string} msRefreshToken The microsoft refresh token
|
||||
* @param {date} msExpires The date when the microsoft access token expires
|
||||
@@ -419,7 +363,6 @@ exports.removeAuthAccount = function(uuid){
|
||||
config.selectedAccount = keys[0]
|
||||
} else {
|
||||
config.selectedAccount = null
|
||||
config.clientToken = null
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -431,20 +431,6 @@ class ProcessBuilder {
|
||||
|
||||
}
|
||||
|
||||
_processAutoConnectArg(args){
|
||||
if(ConfigManager.getAutoConnect() && this.server.rawServer.autoconnect){
|
||||
if(mcVersionAtLeast('1.20', this.server.rawServer.minecraftVersion)){
|
||||
args.push('--quickPlayMultiplayer')
|
||||
args.push(`${this.server.hostname}:${this.server.port}`)
|
||||
} else {
|
||||
args.push('--server')
|
||||
args.push(this.server.hostname)
|
||||
args.push('--port')
|
||||
args.push(this.server.port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the argument array that will be passed to the JVM process.
|
||||
*
|
||||
@@ -616,7 +602,7 @@ class ProcessBuilder {
|
||||
val = this.authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang'
|
||||
val = 'msa'
|
||||
break
|
||||
case 'version_type':
|
||||
val = this.vanillaManifest.type
|
||||
@@ -647,10 +633,6 @@ class ProcessBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Autoconnect
|
||||
this._processAutoConnectArg(args)
|
||||
|
||||
|
||||
// Forge Specific Arguments
|
||||
args = args.concat(this.modManifest.arguments.game)
|
||||
|
||||
@@ -700,7 +682,7 @@ class ProcessBuilder {
|
||||
val = this.authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang'
|
||||
val = 'msa'
|
||||
break
|
||||
case 'user_properties': // 1.8.9 and below.
|
||||
val = '{}'
|
||||
@@ -715,9 +697,6 @@ class ProcessBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Autoconnect to the selected server.
|
||||
this._processAutoConnectArg(mcArgs)
|
||||
|
||||
// Prepare game resolution
|
||||
if(ConfigManager.getFullscreen()){
|
||||
mcArgs.push('--fullscreen')
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// Requirements
|
||||
const { URL } = require('url')
|
||||
const {
|
||||
MojangRestAPI,
|
||||
getServerStatus
|
||||
} = require('helios-core/mojang')
|
||||
const {
|
||||
@@ -176,65 +175,6 @@ server_selection_button.onclick = async e => {
|
||||
await toggleServerSelection(true)
|
||||
}
|
||||
|
||||
// Update Mojang Status Color
|
||||
const refreshMojangStatuses = async function(){
|
||||
loggerLanding.info('Refreshing Mojang Statuses..')
|
||||
|
||||
let status = 'grey'
|
||||
let tooltipEssentialHTML = ''
|
||||
let tooltipNonEssentialHTML = ''
|
||||
|
||||
const response = await MojangRestAPI.status()
|
||||
let statuses
|
||||
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||
statuses = response.data
|
||||
} else {
|
||||
loggerLanding.warn('Unable to refresh Mojang service status.')
|
||||
statuses = MojangRestAPI.getDefaultStatuses()
|
||||
}
|
||||
|
||||
greenCount = 0
|
||||
greyCount = 0
|
||||
|
||||
for(let i=0; i<statuses.length; i++){
|
||||
const service = statuses[i]
|
||||
|
||||
const tooltipHTML = `<div class="mojangStatusContainer">
|
||||
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">•</span>
|
||||
<span class="mojangStatusName">${service.name}</span>
|
||||
</div>`
|
||||
if(service.essential){
|
||||
tooltipEssentialHTML += tooltipHTML
|
||||
} else {
|
||||
tooltipNonEssentialHTML += tooltipHTML
|
||||
}
|
||||
|
||||
if(service.status === 'yellow' && status !== 'red'){
|
||||
status = 'yellow'
|
||||
} else if(service.status === 'red'){
|
||||
status = 'red'
|
||||
} else {
|
||||
if(service.status === 'grey'){
|
||||
++greyCount
|
||||
}
|
||||
++greenCount
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(greenCount === statuses.length){
|
||||
if(greyCount === statuses.length){
|
||||
status = 'grey'
|
||||
} else {
|
||||
status = 'green'
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
|
||||
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
|
||||
document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status)
|
||||
}
|
||||
|
||||
const refreshServerStatus = async (fade = false) => {
|
||||
loggerLanding.info('Refreshing Server Status')
|
||||
const serv = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer())
|
||||
@@ -266,11 +206,8 @@ const refreshServerStatus = async (fade = false) => {
|
||||
|
||||
}
|
||||
|
||||
refreshMojangStatuses()
|
||||
// Server Status is refreshed in uibinder.js on distributionIndexDone.
|
||||
|
||||
// Refresh statuses every hour. The status page itself refreshes every day so...
|
||||
let mojangStatusListener = setInterval(() => refreshMojangStatuses(true), 60*60*1000)
|
||||
// Set refresh rate to once every 5 minutes.
|
||||
let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000)
|
||||
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
/**
|
||||
* Script for login.ejs
|
||||
*/
|
||||
// Validation Regexes.
|
||||
const validUsername = /^[a-zA-Z0-9_]{1,16}$/
|
||||
const basicEmail = /^\S+@\S+\.\S+$/
|
||||
//const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
|
||||
|
||||
// Login Elements
|
||||
const loginCancelContainer = document.getElementById('loginCancelContainer')
|
||||
const loginCancelButton = document.getElementById('loginCancelButton')
|
||||
const loginEmailError = document.getElementById('loginEmailError')
|
||||
const loginUsername = document.getElementById('loginUsername')
|
||||
const loginPasswordError = document.getElementById('loginPasswordError')
|
||||
const loginPassword = document.getElementById('loginPassword')
|
||||
const checkmarkContainer = document.getElementById('checkmarkContainer')
|
||||
const loginRememberOption = document.getElementById('loginRememberOption')
|
||||
const loginButton = document.getElementById('loginButton')
|
||||
const loginForm = document.getElementById('loginForm')
|
||||
|
||||
// Control variables.
|
||||
let lu = false, lp = false
|
||||
|
||||
|
||||
/**
|
||||
* Show a login error.
|
||||
*
|
||||
* @param {HTMLElement} element The element on which to display the error.
|
||||
* @param {string} value The error text.
|
||||
*/
|
||||
function showError(element, value){
|
||||
element.innerHTML = value
|
||||
element.style.opacity = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Shake a login error to add emphasis.
|
||||
*
|
||||
* @param {HTMLElement} element The element to shake.
|
||||
*/
|
||||
function shakeError(element){
|
||||
if(element.style.opacity == 1){
|
||||
element.classList.remove('shake')
|
||||
void element.offsetWidth
|
||||
element.classList.add('shake')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that an email field is neither empty nor invalid.
|
||||
*
|
||||
* @param {string} value The email value.
|
||||
*/
|
||||
function validateEmail(value){
|
||||
if(value){
|
||||
if(!basicEmail.test(value) && !validUsername.test(value)){
|
||||
showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
|
||||
loginDisabled(true)
|
||||
lu = false
|
||||
} else {
|
||||
loginEmailError.style.opacity = 0
|
||||
lu = true
|
||||
if(lp){
|
||||
loginDisabled(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lu = false
|
||||
showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
|
||||
loginDisabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the password field is not empty.
|
||||
*
|
||||
* @param {string} value The password value.
|
||||
*/
|
||||
function validatePassword(value){
|
||||
if(value){
|
||||
loginPasswordError.style.opacity = 0
|
||||
lp = true
|
||||
if(lu){
|
||||
loginDisabled(false)
|
||||
}
|
||||
} else {
|
||||
lp = false
|
||||
showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
|
||||
loginDisabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Emphasize errors with shake when focus is lost.
|
||||
loginUsername.addEventListener('focusout', (e) => {
|
||||
validateEmail(e.target.value)
|
||||
shakeError(loginEmailError)
|
||||
})
|
||||
loginPassword.addEventListener('focusout', (e) => {
|
||||
validatePassword(e.target.value)
|
||||
shakeError(loginPasswordError)
|
||||
})
|
||||
|
||||
// Validate input for each field.
|
||||
loginUsername.addEventListener('input', (e) => {
|
||||
validateEmail(e.target.value)
|
||||
})
|
||||
loginPassword.addEventListener('input', (e) => {
|
||||
validatePassword(e.target.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* Enable or disable the login button.
|
||||
*
|
||||
* @param {boolean} v True to enable, false to disable.
|
||||
*/
|
||||
function loginDisabled(v){
|
||||
if(loginButton.disabled !== v){
|
||||
loginButton.disabled = v
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable loading elements.
|
||||
*
|
||||
* @param {boolean} v True to enable, false to disable.
|
||||
*/
|
||||
function loginLoading(v){
|
||||
if(v){
|
||||
loginButton.setAttribute('loading', v)
|
||||
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
|
||||
} else {
|
||||
loginButton.removeAttribute('loading')
|
||||
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable login form.
|
||||
*
|
||||
* @param {boolean} v True to enable, false to disable.
|
||||
*/
|
||||
function formDisabled(v){
|
||||
loginDisabled(v)
|
||||
loginCancelButton.disabled = v
|
||||
loginUsername.disabled = v
|
||||
loginPassword.disabled = v
|
||||
if(v){
|
||||
checkmarkContainer.setAttribute('disabled', v)
|
||||
} else {
|
||||
checkmarkContainer.removeAttribute('disabled')
|
||||
}
|
||||
loginRememberOption.disabled = v
|
||||
}
|
||||
|
||||
let loginViewOnSuccess = VIEWS.landing
|
||||
let loginViewOnCancel = VIEWS.settings
|
||||
let loginViewCancelHandler
|
||||
|
||||
function loginCancelEnabled(val){
|
||||
if(val){
|
||||
$(loginCancelContainer).show()
|
||||
} else {
|
||||
$(loginCancelContainer).hide()
|
||||
}
|
||||
}
|
||||
|
||||
loginCancelButton.onclick = (e) => {
|
||||
switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
|
||||
loginUsername.value = ''
|
||||
loginPassword.value = ''
|
||||
loginCancelEnabled(false)
|
||||
if(loginViewCancelHandler != null){
|
||||
loginViewCancelHandler()
|
||||
loginViewCancelHandler = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Disable default form behavior.
|
||||
loginForm.onsubmit = () => { return false }
|
||||
|
||||
// Bind login button behavior.
|
||||
loginButton.addEventListener('click', () => {
|
||||
// Disable form.
|
||||
formDisabled(true)
|
||||
|
||||
// Show loading stuff.
|
||||
loginLoading(true)
|
||||
|
||||
AuthManager.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => {
|
||||
updateSelectedAccount(value)
|
||||
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
|
||||
$('.circle-loader').toggleClass('load-complete')
|
||||
$('.checkmark').toggle()
|
||||
setTimeout(() => {
|
||||
switchView(VIEWS.login, loginViewOnSuccess, 500, 500, async () => {
|
||||
// Temporary workaround
|
||||
if(loginViewOnSuccess === VIEWS.settings){
|
||||
await prepareSettings()
|
||||
}
|
||||
loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
|
||||
loginCancelEnabled(false) // Reset this for good measure.
|
||||
loginViewCancelHandler = null // Reset this for good measure.
|
||||
loginUsername.value = ''
|
||||
loginPassword.value = ''
|
||||
$('.circle-loader').toggleClass('load-complete')
|
||||
$('.checkmark').toggle()
|
||||
loginLoading(false)
|
||||
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
|
||||
formDisabled(false)
|
||||
})
|
||||
}, 1000)
|
||||
}).catch((displayableError) => {
|
||||
loginLoading(false)
|
||||
|
||||
let actualDisplayableError
|
||||
if(isDisplayableError(displayableError)) {
|
||||
msftLoginLogger.error('Error while logging in.', displayableError)
|
||||
actualDisplayableError = displayableError
|
||||
} else {
|
||||
// Uh oh.
|
||||
msftLoginLogger.error('Unhandled error during login.', displayableError)
|
||||
actualDisplayableError = Lang.queryJS('login.error.unknown')
|
||||
}
|
||||
|
||||
setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain'))
|
||||
setOverlayHandler(() => {
|
||||
formDisabled(false)
|
||||
toggleOverlay(false)
|
||||
})
|
||||
toggleOverlay(true)
|
||||
})
|
||||
|
||||
})
|
||||
@@ -502,32 +502,10 @@ let msAccDomElementCache
|
||||
function processLogOut(val, isLastAccount){
|
||||
const parent = val.closest('.settingsAuthAccount')
|
||||
const uuid = parent.getAttribute('uuid')
|
||||
const prevSelAcc = ConfigManager.getSelectedAccount()
|
||||
const targetAcc = ConfigManager.getAuthAccount(uuid)
|
||||
if(targetAcc.type === 'microsoft') {
|
||||
msAccDomElementCache = parent
|
||||
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
|
||||
ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount)
|
||||
})
|
||||
} else {
|
||||
AuthManager.removeMojangAccount(uuid).then(() => {
|
||||
if(!isLastAccount && uuid === prevSelAcc.uuid){
|
||||
const selAcc = ConfigManager.getSelectedAccount()
|
||||
refreshAuthAccountSelected(selAcc.uuid)
|
||||
updateSelectedAccount(selAcc)
|
||||
validateSelectedAccount()
|
||||
}
|
||||
if(isLastAccount) {
|
||||
loginOptionsCancelEnabled(false)
|
||||
loginOptionsViewOnLoginSuccess = VIEWS.settings
|
||||
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
|
||||
switchView(getCurrentView(), VIEWS.loginOptions)
|
||||
}
|
||||
})
|
||||
$(parent).fadeOut(250, () => {
|
||||
parent.remove()
|
||||
})
|
||||
}
|
||||
msAccDomElementCache = parent
|
||||
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
|
||||
ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount)
|
||||
})
|
||||
}
|
||||
|
||||
// Bind reply for Microsoft Logout.
|
||||
|
||||
@@ -17,7 +17,6 @@ let fatalStartupError = false
|
||||
const VIEWS = {
|
||||
landing: '#landingContainer',
|
||||
loginOptions: '#loginOptionsContainer',
|
||||
login: '#loginContainer',
|
||||
settings: '#settingsContainer',
|
||||
welcome: '#welcomeContainer',
|
||||
waiting: '#waitingContainer'
|
||||
@@ -336,36 +335,21 @@ async function validateSelectedAccount(){
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
|
||||
const isMicrosoft = selectedAcc.type === 'microsoft'
|
||||
|
||||
if(isMicrosoft) {
|
||||
// Empty for now
|
||||
} else {
|
||||
// Mojang
|
||||
// For convenience, pre-populate the username of the account.
|
||||
document.getElementById('loginUsername').value = selectedAcc.username
|
||||
validateEmail(selectedAcc.username)
|
||||
}
|
||||
|
||||
loginOptionsViewOnLoginSuccess = getCurrentView()
|
||||
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
|
||||
|
||||
if(accLen > 0) {
|
||||
loginOptionsViewOnCancel = getCurrentView()
|
||||
loginOptionsViewCancelHandler = () => {
|
||||
if(isMicrosoft) {
|
||||
ConfigManager.addMicrosoftAuthAccount(
|
||||
selectedAcc.uuid,
|
||||
selectedAcc.accessToken,
|
||||
selectedAcc.username,
|
||||
selectedAcc.expiresAt,
|
||||
selectedAcc.microsoft.access_token,
|
||||
selectedAcc.microsoft.refresh_token,
|
||||
selectedAcc.microsoft.expires_at
|
||||
)
|
||||
} else {
|
||||
ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
|
||||
}
|
||||
ConfigManager.addMicrosoftAuthAccount(
|
||||
selectedAcc.uuid,
|
||||
selectedAcc.accessToken,
|
||||
selectedAcc.username,
|
||||
selectedAcc.expiresAt,
|
||||
selectedAcc.microsoft.access_token,
|
||||
selectedAcc.microsoft.refresh_token,
|
||||
selectedAcc.microsoft.expires_at
|
||||
)
|
||||
ConfigManager.save()
|
||||
validateSelectedAccount()
|
||||
}
|
||||
|
||||
@@ -39,25 +39,6 @@
|
||||
<span class="bot_label" id="landingPlayerLabel"><%- lang('landing.serverStatus') %></span>
|
||||
<span id="player_count"><%- lang('landing.serverStatusPlaceholder') %></span>
|
||||
</div>
|
||||
<div class="bot_divider"></div>
|
||||
<div id="mojangStatusWrapper">
|
||||
<span class="bot_label"><%- lang('landing.mojangStatus') %></span>
|
||||
<span id="mojang_status_icon">•</span>
|
||||
<div id="mojangStatusTooltip">
|
||||
<div id="mojangStatusTooltipTitle"><%- lang('landing.mojangStatusTooltipTitle') %></div>
|
||||
<div id="mojangStatusEssentialContainer">
|
||||
<!-- Essential Mojang services are populated here. -->
|
||||
</div>
|
||||
<div id="mojangStatusNEContainer">
|
||||
<div class="mojangStatusNEBar"></div>
|
||||
<div id="mojangStatusNETitle"><%- lang('landing.mojangStatusNETitle') %></div>
|
||||
<div class="mojangStatusNEBar"></div>
|
||||
</div>
|
||||
<div id="mojangStatusNonEssentialContainer">
|
||||
<!-- Non Essential Mojang services are populated here. -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<div id="loginContainer" style="display: none;">
|
||||
<div id="loginCancelContainer" style="display: none;">
|
||||
<button id="loginCancelButton">
|
||||
<div id="loginCancelIcon">X</div>
|
||||
<span id="loginCancelText"><%- lang('login.loginCancelText') %></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="loginContent">
|
||||
<form id="loginForm">
|
||||
<img id="loginImageSeal" src="assets/images/SealCircle.png"/>
|
||||
<span id="loginSubheader"><%- lang('login.loginSubheader') %></span>
|
||||
<div class="loginFieldContainer">
|
||||
<svg id="profileSVG" class="loginSVG" viewBox="40 37 65.36 61.43">
|
||||
<g>
|
||||
<path d="M86.77,58.12A13.79,13.79,0,1,0,73,71.91,13.79,13.79,0,0,0,86.77,58.12M97,103.67a3.41,3.41,0,0,0,3.39-3.84,27.57,27.57,0,0,0-54.61,0,3.41,3.41,0,0,0,3.39,3.84Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="loginErrorSpan" id="loginEmailError"><%- lang('login.loginEmailError') %></span>
|
||||
<input id="loginUsername" class="loginField" type="text" placeholder="<%- lang('login.loginEmailPlaceholder') %>"/>
|
||||
</div>
|
||||
<div class="loginFieldContainer">
|
||||
<svg id="lockSVG" class="loginSVG" viewBox="40 32 60.36 70.43">
|
||||
<g>
|
||||
<path d="M86.16,54a16.38,16.38,0,1,0-32,0H44V102.7H96V54Zm-25.9-3.39a9.89,9.89,0,1,1,19.77,0A9.78,9.78,0,0,1,79.39,54H60.89A9.78,9.78,0,0,1,60.26,50.59ZM70,96.2a6.5,6.5,0,0,1-6.5-6.5,6.39,6.39,0,0,1,3.1-5.4V67h6.5V84.11a6.42,6.42,0,0,1,3.39,5.6A6.5,6.5,0,0,1,70,96.2Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<span class="loginErrorSpan" id="loginPasswordError"><%- lang('login.loginPasswordError') %></span>
|
||||
<input id="loginPassword" class="loginField" type="password" placeholder="<%- lang('login.loginPasswordPlaceholder') %>"/>
|
||||
</div>
|
||||
<div id="loginOptions">
|
||||
<span class="loginSpanDim">
|
||||
<a href="<%- lang('login.loginForgotPasswordLink') %>"><%- lang('login.loginForgotPasswordText') %></a>
|
||||
</span>
|
||||
<label id="checkmarkContainer">
|
||||
<input id="loginRememberOption" type="checkbox" checked>
|
||||
<span id="loginRememberText" class="loginSpanDim"><%- lang('login.loginRememberMeText') %></span>
|
||||
<span class="loginCheckmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<button id="loginButton" disabled>
|
||||
<div id="loginButtonContent">
|
||||
<%- lang('login.loginButtonText') %>
|
||||
<svg id="loginSVG" viewBox="0 0 24.87 13.97">
|
||||
<defs>
|
||||
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;transition: 0.25s ease;}</style>
|
||||
</defs>
|
||||
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
|
||||
</svg>
|
||||
<div class="circle-loader">
|
||||
<div class="checkmark draw"></div>
|
||||
</div>
|
||||
<!--<div class="spinningCircle" id="loginSpinner"></div>-->
|
||||
</div>
|
||||
</button>
|
||||
<div id="loginDisclaimer">
|
||||
<span class="loginSpanDim" id="loginRegisterSpan">
|
||||
<a href="<%- lang('login.loginNeedAccountLink') %>"><%- lang('login.loginNeedAccountText') %></a>
|
||||
</span>
|
||||
<p class="loginDisclaimerText"><%- lang('login.loginPasswordDisclaimer1') %></p>
|
||||
<p class="loginDisclaimerText"><%- lang('login.loginPasswordDisclaimer2', { appName: lang('app.title') }) %></p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script src="./assets/js/scripts/login.js"></script>
|
||||
</div>
|
||||
@@ -73,17 +73,6 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settingsFieldContainer">
|
||||
<div class="settingsFieldLeft">
|
||||
<span class="settingsFieldTitle"><%- lang('settings.autoConnectTitle') %></span>
|
||||
</div>
|
||||
<div class="settingsFieldRight">
|
||||
<label class="toggleSwitch">
|
||||
<input type="checkbox" cValue="AutoConnect">
|
||||
<span class="toggleSwitchSlider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settingsFieldContainer">
|
||||
<div class="settingsFieldLeft">
|
||||
<span class="settingsFieldTitle"><%- lang('settings.launchDetachedTitle') %></span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "surviepote-launcher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"productName": "SurviePote",
|
||||
"description": "Launcher officiel SurviePote - Rejoins mcpv.ihorizon.org",
|
||||
"author": "SurviePote",
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# nosingleplayer (mod maison SurviePote)
|
||||
|
||||
Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`).
|
||||
Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`) et adapte l'UI
|
||||
au serveur unique :
|
||||
- Menu titre : bouton **Rejoindre SurviePote** (connexion directe à `mcpv.ihorizon.org:25565`,
|
||||
sans transfert) ; Solo, Multi, Realms, réseau social et icônes bas retirés.
|
||||
- Menu Échap : le bouton de déconnexion devient **Quitter SurviePote** et renvoie à l'écran
|
||||
d'accueil du jeu (et non la liste des serveurs).
|
||||
- MC 26.3, Fabric Loader 0.20.0, Java 25, `environment: client`.
|
||||
- Particularité 26.x : Mojang ne publie plus de mappings (ni Yarn ni `client_mappings`) et le
|
||||
jeu n'est plus obfusqué → **pas de Loom**, compilation directe contre le jar client Mojang
|
||||
@@ -8,4 +13,4 @@ Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`).
|
||||
pas de refmap nécessaire.
|
||||
|
||||
Build : `cd mods/nosingleplayer && ./gradlew build` (dans nix-shell racine pour Java 25).
|
||||
Jar : `build/libs/nosingleplayer-1.0.0.jar` → copié dans `atlas/.../fabricmods/required/`.
|
||||
Jar : `build/libs/nosingleplayer-1.1.4.jar` → copié dans `atlas/.../fabricmods/required/`.
|
||||
|
||||
@@ -42,6 +42,9 @@ tasks.register('downloadMcLibs') {
|
||||
new File(outDir, 'slf4j-api.jar'))
|
||||
dl('https://libraries.minecraft.net/com/mojang/brigadier/1.3.11/brigadier-1.3.11.jar',
|
||||
new File(outDir, 'brigadier.jar'))
|
||||
// Annotations JSpecify : referencées par les signatures de MC 26.3 (@Nullable...).
|
||||
dl('https://repo1.maven.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar',
|
||||
new File(outDir, 'jspecify.jar'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,3 +67,97 @@ processResources {
|
||||
java {
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dev : lance le vrai client MC 26.3 + nosingleplayer (build sans loom).
|
||||
// Réutilise l'installation commune du launcher SurviePote (~/.surviepote/common :
|
||||
// versions, libraries, assets — remplie au premier lancement). Le mod est copié
|
||||
// dans runClient/game/mods/ (Knot le découvre comme un mod Fabric classique).
|
||||
// Usage : ./gradlew runClient
|
||||
// ---------------------------------------------------------------------------
|
||||
def survieCommon = new File(System.getProperty('user.home'), '.surviepote/common')
|
||||
def mcVersion = project.minecraft_version
|
||||
def loaderVersion = project.loader_version
|
||||
|
||||
tasks.register('extractClientNatives', Copy) {
|
||||
def versionJson = new groovy.json.JsonSlurper().parse(
|
||||
new File(survieCommon, "versions/${mcVersion}/${mcVersion}.json"))
|
||||
from(versionJson.libraries.findAll { lib ->
|
||||
lib.name.contains(':natives-linux') && (lib.rules == null || lib.rules.any { it.action == 'allow' && (it.os == null || it.os.name == 'linux') })
|
||||
}.collect { lib ->
|
||||
zipTree(new File(survieCommon, "libraries/${lib.downloads.artifact.path}"))
|
||||
})
|
||||
into(layout.buildDirectory.dir('runClient/natives'))
|
||||
include('**/*.so')
|
||||
eachFile { f -> f.path = f.name }
|
||||
}
|
||||
|
||||
tasks.register('runClient', JavaExec) {
|
||||
dependsOn('extractClientNatives', 'jar')
|
||||
group = 'fabric'
|
||||
description = 'Lance le client Minecraft avec le mod (dev visuel, sans loom).'
|
||||
|
||||
def versionJson = new groovy.json.JsonSlurper().parse(
|
||||
new File(survieCommon, "versions/${mcVersion}/${mcVersion}.json"))
|
||||
def fabricJson = new groovy.json.JsonSlurper().parse(
|
||||
new File(survieCommon, "versions/${mcVersion}-fabric-${loaderVersion}/${mcVersion}-fabric-${loaderVersion}.json"))
|
||||
def mavenToPath = { String coord ->
|
||||
def parts = coord.split(':')
|
||||
return "${parts[0].replace('.', '/')}/${parts[1]}/${parts[2]}/${parts[1]}-${parts[2]}.jar"
|
||||
}
|
||||
|
||||
doFirst {
|
||||
def modsDir = layout.buildDirectory.dir('runClient/game/mods').get().asFile
|
||||
delete(modsDir)
|
||||
modsDir.mkdirs()
|
||||
copy {
|
||||
from(tasks.named('jar'))
|
||||
into(modsDir)
|
||||
}
|
||||
}
|
||||
|
||||
// NixOS : dlopen ne voit ni libGL ni libvulkan sans LD_LIBRARY_PATH (cf launcher).
|
||||
def nixStore = new File('/nix/store')
|
||||
def glDirs = []
|
||||
['/run/opengl-driver/lib', '/run/current-system/sw/lib'].each { d ->
|
||||
if (new File(d).exists()) glDirs << d
|
||||
}
|
||||
if (nixStore.exists()) {
|
||||
nixStore.eachDir { e ->
|
||||
def n = e.name
|
||||
if (n.contains('libglvnd') || n.contains('vulkan-loader') || n.contains('alsa-lib')
|
||||
|| n.contains('pulseaudio') || (n.contains('pipewire') && !n.contains('python'))) {
|
||||
def lib = new File(e, 'lib')
|
||||
if (lib.exists()) glDirs << lib.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
def prevLd = System.getenv('LD_LIBRARY_PATH')
|
||||
environment('LD_LIBRARY_PATH', (prevLd ? prevLd + ':' : '') + glDirs.join(':'))
|
||||
if (!System.getenv('SDL_VIDEODRIVER') && (System.getenv('WAYLAND_DISPLAY') || new File('/run/user/1000/wayland-1').exists())) {
|
||||
environment('SDL_VIDEODRIVER', 'wayland')
|
||||
}
|
||||
|
||||
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(25) }
|
||||
classpath = files(
|
||||
new File(survieCommon, "versions/${mcVersion}/${mcVersion}.jar"),
|
||||
versionJson.libraries.findAll { lib ->
|
||||
!(lib.name.contains(':natives-')) && (lib.rules == null || lib.rules.any { it.action == 'allow' && (it.os == null || it.os.name == 'linux') })
|
||||
}.collect { new File(survieCommon, "libraries/${it.downloads.artifact.path}") },
|
||||
fabricJson.libraries.collect { new File(survieCommon, "libraries/${mavenToPath(it.name)}") }
|
||||
)
|
||||
mainClass = 'net.fabricmc.loader.impl.launch.knot.KnotClient'
|
||||
args = [
|
||||
'--gameDir', layout.buildDirectory.dir('runClient/game').get().asFile.absolutePath,
|
||||
'--assetsDir', new File(survieCommon, 'assets').absolutePath,
|
||||
'--assetIndex', versionJson.assetIndex.id,
|
||||
'--version', "${project.archives_base_name}-dev",
|
||||
'--username', 'Dev', '--uuid', '00000000-0000-0000-0000-000000000000',
|
||||
'--accessToken', '0', '--versionType', 'release'
|
||||
]
|
||||
jvmArgs = [
|
||||
"-Djava.library.path=${layout.buildDirectory.dir('runClient/natives').get().asFile.absolutePath}",
|
||||
'-Xmx4G'
|
||||
]
|
||||
systemProperties['org.lwjgl.util.Debug'] = 'false'
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ loader_version=0.20.0
|
||||
loom_version=1.19.1
|
||||
|
||||
# Mod Properties
|
||||
mod_version=1.1.2
|
||||
mod_version=1.1.8
|
||||
maven_group=fr.surviepote
|
||||
archives_base_name=nosingleplayer
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package fr.surviepote.nosingleplayer;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.renderpearl.api.textures.FilterMode;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* Chargement des textures du mod.
|
||||
*
|
||||
* <p>MC 26.3 n'expose plus les ressources des mods au ResourceManager : les packs ne
|
||||
* sont lus que depuis des dossiers, pas depuis les jars du classpath. Une texture
|
||||
* référencée par son Identifier est donc introuvable (placeholder magenta). On lit
|
||||
* donc le PNG directement dans le jar du mod et on enregistre une DynamicTexture dans
|
||||
* le TextureManager.
|
||||
*/
|
||||
public final class ModTextures {
|
||||
|
||||
private static final Map<Identifier, DynamicTexture> CACHE = new HashMap<>();
|
||||
|
||||
private ModTextures() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renvoie la texture (chargée depuis le jar du mod), ou null si le fichier est absent.
|
||||
*
|
||||
* @param id identifiant sous lequel la texture est enregistrée
|
||||
* @param resourcePath chemin dans le jar, ex {@code /assets/nosingleplayer/...}
|
||||
* @param name nom affiché dans les crash reports GPU
|
||||
* @param linear true = filtrage linéaire (images agrandies), false = nearest (pixel art)
|
||||
*/
|
||||
public static DynamicTexture get(Identifier id, String resourcePath, String name, boolean linear) {
|
||||
DynamicTexture texture = CACHE.get(id);
|
||||
var textures = Minecraft.getInstance().getTextureManager();
|
||||
if (texture != null && textures.getTexture(id) == texture) {
|
||||
return texture; // déjà chargée et toujours enregistrée
|
||||
}
|
||||
try (InputStream in = ModTextures.class.getResourceAsStream(resourcePath)) {
|
||||
if (in == null) {
|
||||
NoSingleplayer.LOGGER.error("[SurviePote] Texture introuvable dans le jar : {}", resourcePath);
|
||||
return null;
|
||||
}
|
||||
NativeImage image = NativeImage.read(in);
|
||||
texture = linear
|
||||
? new LinearTexture(() -> name, image)
|
||||
: new DynamicTexture(() -> name, image);
|
||||
CACHE.put(id, texture);
|
||||
textures.register(id, texture);
|
||||
return texture;
|
||||
} catch (IOException e) {
|
||||
NoSingleplayer.LOGGER.error("[SurviePote] Échec de chargement de la texture " + resourcePath, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** DynamicTexture avec filtrage linéaire (utile quand l'image est réduite à l'écran). */
|
||||
private static final class LinearTexture extends DynamicTexture {
|
||||
LinearTexture(java.util.function.Supplier<String> name, NativeImage image) {
|
||||
super(name, image);
|
||||
this.sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.LINEAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package fr.surviepote.nosingleplayer.gui;
|
||||
|
||||
import fr.surviepote.nosingleplayer.ModTextures;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.gui.components.AbstractButton;
|
||||
import net.minecraft.client.input.InputWithModifiers;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* Bouton "Join Discord" : fond vanilla, logo Discord en pixel art et libellé.
|
||||
* Ouvre l'invite Discord au clic.
|
||||
*/
|
||||
public class DiscordButton extends AbstractButton {
|
||||
|
||||
public static final Identifier TEXTURE = Identifier.fromNamespaceAndPath(
|
||||
"nosingleplayer", "textures/gui/discord.png");
|
||||
|
||||
private static final String TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/discord.png";
|
||||
|
||||
private static final int ICON_SIZE = 16;
|
||||
private static final int ICON_GAP = 5;
|
||||
private static final int TEXT_COLOR = 0xFFFFFFFF;
|
||||
private static final int TEXT_HEIGHT = 9;
|
||||
|
||||
private final Runnable action;
|
||||
|
||||
public DiscordButton(int x, int y, int width, int height, String label, Runnable action) {
|
||||
super(x, y, width, height, Component.literal(label));
|
||||
this.action = action;
|
||||
// La texture doit être enregistrée avant le premier rendu.
|
||||
ModTextures.get(TEXTURE, TEXTURE_PATH, "SurviePote Discord", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extractContents(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
|
||||
extractDefaultSprite(extractor); // fond standard (survol/clic inclus)
|
||||
|
||||
Font font = Minecraft.getInstance().font;
|
||||
String label = getMessage().getString();
|
||||
int textWidth = font.width(label);
|
||||
int groupWidth = ICON_SIZE + ICON_GAP + textWidth;
|
||||
int startX = getX() + (getWidth() - groupWidth) / 2;
|
||||
int iconY = getY() + (getHeight() - ICON_SIZE) / 2;
|
||||
int textY = getY() + (getHeight() - TEXT_HEIGHT) / 2;
|
||||
|
||||
extractor.blit(RenderPipelines.GUI_TEXTURED, TEXTURE,
|
||||
startX, iconY, 0.0F, 0.0F, ICON_SIZE, ICON_SIZE, ICON_SIZE, ICON_SIZE, -1);
|
||||
extractor.text(font, label, startX + ICON_SIZE + ICON_GAP, textY, TEXT_COLOR, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPress(InputWithModifiers input) {
|
||||
this.action.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateWidgetNarration(NarrationElementOutput output) {
|
||||
defaultButtonNarrationText(output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package fr.surviepote.nosingleplayer.gui;
|
||||
|
||||
import fr.surviepote.nosingleplayer.ModTextures;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.gui.components.AbstractButton;
|
||||
import net.minecraft.client.input.InputWithModifiers;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* Bouton icône vanilla (fond standard) avec une texture du mod, sans texte.
|
||||
*/
|
||||
public class IconButton extends AbstractButton {
|
||||
|
||||
public static final Identifier OPTIONS_TEXTURE = Identifier.fromNamespaceAndPath(
|
||||
"nosingleplayer", "textures/gui/options.png");
|
||||
public static final Identifier QUIT_TEXTURE = Identifier.fromNamespaceAndPath(
|
||||
"nosingleplayer", "textures/gui/quit.png");
|
||||
|
||||
private static final int ICON_SIZE = 16;
|
||||
|
||||
private final Identifier texture;
|
||||
private final Runnable action;
|
||||
|
||||
public IconButton(int x, int y, int width, int height, Component message,
|
||||
Identifier texture, String resourcePath, Runnable action) {
|
||||
super(x, y, width, height, message);
|
||||
this.texture = texture;
|
||||
this.action = action;
|
||||
// La texture doit être enregistrée avant le premier rendu.
|
||||
ModTextures.get(texture, resourcePath, message.getString(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extractContents(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
|
||||
extractDefaultSprite(extractor); // fond standard (survol/clic inclus)
|
||||
int iconX = getX() + (getWidth() - ICON_SIZE) / 2;
|
||||
int iconY = getY() + (getHeight() - ICON_SIZE) / 2;
|
||||
extractor.blit(RenderPipelines.GUI_TEXTURED, this.texture,
|
||||
iconX, iconY, 0.0F, 0.0F, ICON_SIZE, ICON_SIZE, ICON_SIZE, ICON_SIZE, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPress(InputWithModifiers input) {
|
||||
this.action.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateWidgetNarration(NarrationElementOutput output) {
|
||||
defaultButtonNarrationText(output);
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package fr.surviepote.nosingleplayer.gui;
|
||||
|
||||
import fr.surviepote.nosingleplayer.NoSingleplayer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.multiplayer.ServerStatusPinger;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.network.EventLoopGroupHolder;
|
||||
|
||||
/**
|
||||
* Panneau "statut du serveur" affiché en haut à gauche du menu principal :
|
||||
* état en ligne / hors ligne, nombre de joueurs et ping.
|
||||
*
|
||||
* <p>Note : dans ServerStatusPinger.pingServer, les deux Runnable passés ne sont pas
|
||||
* des callbacks de succès/échec (le 1er ne fire que si le favicon change, le 2nd est
|
||||
* le pong). On lit donc directement l'état dans ServerData : `status` contient le
|
||||
* compteur de joueurs ("0/20") quand le statut est arrivé, et est vidé en cas d'échec.
|
||||
*/
|
||||
public class ServerStatusWidget extends AbstractWidget {
|
||||
|
||||
private static final String HOST = "mcpv.ihorizon.org:25565";
|
||||
private static final long REFRESH_MS = 15000L;
|
||||
private static final long TIMEOUT_MS = 8000L;
|
||||
|
||||
private static final int BG_COLOR = 0xB0000000;
|
||||
private static final int ONLINE_COLOR = 0xFF4CD964;
|
||||
private static final int OFFLINE_COLOR = 0xFFE04B4B;
|
||||
private static final int PENDING_COLOR = 0xFFAAAAAA;
|
||||
private static final int TEXT_COLOR = 0xFFFFFFFF;
|
||||
|
||||
private final ServerStatusPinger pinger = new ServerStatusPinger();
|
||||
private final ServerData data = new ServerData("SurviePote", HOST, ServerData.Type.OTHER);
|
||||
|
||||
private volatile boolean resolved;
|
||||
private volatile boolean online;
|
||||
private volatile String playersText = "";
|
||||
private volatile long ping = -1L;
|
||||
|
||||
private long nextRefresh;
|
||||
private long pingStartedAt;
|
||||
private boolean pending;
|
||||
|
||||
public ServerStatusWidget(int x, int y, int width, int height) {
|
||||
super(x, y, width, height, Component.literal("Statut du serveur SurviePote"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extractWidgetRenderState(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now >= this.nextRefresh) {
|
||||
this.nextRefresh = now + REFRESH_MS;
|
||||
refresh();
|
||||
}
|
||||
if (this.pending && now - this.pingStartedAt > TIMEOUT_MS) {
|
||||
this.pending = false; // aucune réponse du serveur
|
||||
}
|
||||
this.pinger.tick();
|
||||
readStatus();
|
||||
|
||||
Font font = Minecraft.getInstance().font;
|
||||
boolean online = this.online;
|
||||
boolean resolved = this.resolved;
|
||||
|
||||
String statusText = !resolved ? "Connexion..." : (online ? "En ligne" : "Hors ligne");
|
||||
int statusColor = !resolved ? PENDING_COLOR : (online ? ONLINE_COLOR : OFFLINE_COLOR);
|
||||
String playersText = online
|
||||
? (this.playersText.matches("\\d+/\\d+") ? this.playersText + " joueurs" : this.playersText)
|
||||
: (resolved ? "Joueurs : -" : "");
|
||||
String pingText = online && this.ping > 0 ? this.ping + " ms" : "";
|
||||
|
||||
// Fond du panneau.
|
||||
extractor.fill(getX(), getY(), getX() + getWidth(), getY() + getHeight(), BG_COLOR);
|
||||
// Pastille d'état.
|
||||
extractor.fill(getX() + 4, getY() + 4, getX() + 9, getY() + 9, statusColor);
|
||||
// Textes.
|
||||
extractor.text(font, statusText, getX() + 12, getY() + 3, statusColor, true);
|
||||
if (!playersText.isEmpty()) {
|
||||
extractor.text(font, playersText, getX() + 12, getY() + 13, TEXT_COLOR, true);
|
||||
}
|
||||
if (!pingText.isEmpty()) {
|
||||
extractor.text(font, pingText,
|
||||
getX() + getWidth() - font.width(pingText) - 4, getY() + 3, PENDING_COLOR, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Lit l'état réel du ping dans ServerData (mis à jour par le thread réseau). */
|
||||
private void readStatus() {
|
||||
Component status = this.data.status;
|
||||
String text = status != null ? status.getString() : "";
|
||||
if (!text.isEmpty()) {
|
||||
this.online = true;
|
||||
this.resolved = true;
|
||||
this.playersText = text; // "0/20"
|
||||
} else if (this.resolved) {
|
||||
this.online = false; // le status a été vidé => échec du ping
|
||||
} else if (!this.pending) {
|
||||
this.resolved = true; // timeout : serveur injoignable
|
||||
}
|
||||
if (this.data.ping > 0) {
|
||||
this.ping = this.data.ping;
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
try {
|
||||
this.pingStartedAt = System.currentTimeMillis();
|
||||
this.pending = true;
|
||||
this.pinger.pingServer(this.data, () -> {
|
||||
}, () -> {
|
||||
}, EventLoopGroupHolder.remote(Minecraft.getInstance().options.useNativeTransport()));
|
||||
} catch (Exception e) {
|
||||
NoSingleplayer.LOGGER.warn("[SurviePote] Ping serveur impossible : {}", e.toString());
|
||||
this.pending = false;
|
||||
this.resolved = true;
|
||||
this.online = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateWidgetNarration(NarrationElementOutput output) {
|
||||
defaultButtonNarrationText(output);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
/** Permet de récupérer l'action d'un bouton vanilla (pour la conserver). */
|
||||
@Mixin(Button.class)
|
||||
public interface ButtonAccessor {
|
||||
|
||||
@Accessor("onPress")
|
||||
Button.OnPress surviepote$getOnPress();
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import fr.surviepote.nosingleplayer.NoSingleplayer;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Titre de la fenêtre : "Minecraft* 26.3" -> "SurviePote [26.3]".
|
||||
* Minecraft.createTitle() est privé, appelé au démarrage (création de la fenêtre)
|
||||
* et par updateTitle() (changement de monde, focus, resize...).
|
||||
*/
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class MinecraftTitleMixin {
|
||||
|
||||
@Unique
|
||||
private static boolean surviepote$titleLogged;
|
||||
|
||||
@Inject(method = "createTitle", at = @At("HEAD"), cancellable = true)
|
||||
private void surviepote$customWindowTitle(CallbackInfoReturnable<String> cir) {
|
||||
String title = "SurviePote [" + SharedConstants.getCurrentVersion().name() + "]";
|
||||
cir.setReturnValue(title);
|
||||
if (!surviepote$titleLogged) {
|
||||
surviepote$titleLogged = true;
|
||||
NoSingleplayer.LOGGER.info("[SurviePote] Titre de fenêtre : {}", title);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.PauseScreen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(PauseScreen.class)
|
||||
public abstract class PauseScreenMixin {
|
||||
|
||||
@Unique
|
||||
private static final Component SURVIEPOTE_QUIT = Component.literal("Quitter SurviePote");
|
||||
|
||||
// Quitte le monde comme le bouton vanilla, mais renvoie vers l'écran d'accueil
|
||||
// (au lieu de la liste des serveurs pour un serveur distant).
|
||||
@Unique
|
||||
private static void surviepote$quitToTitle(Button button) {
|
||||
button.active = false;
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
// Conserve la gestion vanilla du brouillon de signalement, puis redirige.
|
||||
minecraft.getReportingContext().draftReportHandled(minecraft, minecraft.gui.screen(), () -> {
|
||||
minecraft.disconnectFromWorld(ClientLevel.DEFAULT_QUIT_MESSAGE);
|
||||
minecraft.setScreenAndShow(new TitleScreen());
|
||||
}, true);
|
||||
}
|
||||
|
||||
@Redirect(method = "createPauseMenu",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/components/Button;builder(Lnet/minecraft/network/chat/Component;Lnet/minecraft/client/gui/components/Button$OnPress;)Lnet/minecraft/client/gui/components/Button$Builder;"))
|
||||
private static Button.Builder surviepote$relabelDisconnect(Component message, Button.OnPress onPress) {
|
||||
// createPauseMenu ne crée que deux boutons via Button.builder : "Retour au jeu"
|
||||
// et "Déconnexion". On ne remplace que ce dernier.
|
||||
if (message.getString().equals(CommonComponents.GUI_DISCONNECT.getString())) {
|
||||
return Button.builder(SURVIEPOTE_QUIT, PauseScreenMixin::surviepote$quitToTitle);
|
||||
}
|
||||
return Button.builder(message, onPress);
|
||||
}
|
||||
}
|
||||
+87
-26
@@ -1,8 +1,12 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import fr.surviepote.nosingleplayer.gui.DiscordButton;
|
||||
import fr.surviepote.nosingleplayer.gui.IconButton;
|
||||
import fr.surviepote.nosingleplayer.gui.ServerStatusWidget;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
@@ -13,7 +17,6 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.multiplayer.TransferState;
|
||||
import net.minecraft.client.multiplayer.resolver.ServerAddress;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import fr.surviepote.nosingleplayer.NoSingleplayer;
|
||||
@@ -28,6 +31,12 @@ public abstract class TitleScreenMixin {
|
||||
|
||||
private static final String SERVER_HOST = "mcpv.ihorizon.org:25565";
|
||||
|
||||
private static final String DISCORD_URL = "https://discord.gg/EpkgEPv5c";
|
||||
|
||||
private static final int ICON_SIZE = 20;
|
||||
private static final String OPTIONS_TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/options.png";
|
||||
private static final String QUIT_TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/quit.png";
|
||||
|
||||
@Unique
|
||||
private static void surviepote$removeWidget(TitleScreen self, GuiEventListener widget) {
|
||||
self.children().remove(widget);
|
||||
@@ -42,35 +51,22 @@ public abstract class TitleScreenMixin {
|
||||
String solo = Component.translatable("menu.singleplayer").getString();
|
||||
String multi = Component.translatable("menu.multiplayer").getString();
|
||||
String realms = Component.translatable("menu.online").getString();
|
||||
String options = Component.translatable("menu.options").getString();
|
||||
String quit = Component.translatable("menu.quit").getString();
|
||||
|
||||
int joinX = minecraft.getWindow().getGuiScaledWidth() / 2 - 100;
|
||||
int joinY = minecraft.getWindow().getGuiScaledHeight() / 4 + 72;
|
||||
boolean foundMulti = false;
|
||||
|
||||
List<GuiEventListener> toRemove = new ArrayList<>();
|
||||
// TODO DEBUG temporaire : lister les widgets du menu titre.
|
||||
NoSingleplayer.LOGGER.info("[SurviePote] init TAIL, {} widgets:", self.children().size());
|
||||
for (GuiEventListener c : self.children()) {
|
||||
String desc = c.getClass().getSimpleName();
|
||||
if (c instanceof Button b) {
|
||||
desc += " msg=[" + b.getMessage().getString() + "]";
|
||||
}
|
||||
NoSingleplayer.LOGGER.info("[SurviePote] - {}", desc);
|
||||
}
|
||||
Button optionsButton = null;
|
||||
Button quitButton = null;
|
||||
for (GuiEventListener child : new ArrayList<>(self.children())) {
|
||||
if (child instanceof Button button) {
|
||||
String msg = button.getMessage().getString();
|
||||
if (msg.equals(solo)) {
|
||||
button.active = false;
|
||||
} else if (msg.equals(multi)) {
|
||||
joinX = button.getX();
|
||||
joinY = button.getY();
|
||||
foundMulti = true;
|
||||
toRemove.add(button);
|
||||
} else if (msg.equals(realms)) {
|
||||
toRemove.add(button);
|
||||
}
|
||||
} else if (child instanceof FriendsButton) {
|
||||
// ATTENTION : FriendsButton/SpriteIconButton/PlainTextButton héritent tous
|
||||
// de Button. Il faut donc les tester AVANT "instanceof Button", sinon la
|
||||
// première branche les avale et ils ne sont jamais retirés.
|
||||
if (child instanceof FriendsButton) {
|
||||
// Bouton "amis" (interactions sociales).
|
||||
toRemove.add(child);
|
||||
} else if (child instanceof SpriteIconButton) {
|
||||
// Bas-gauche : langue, accessibilité, etc.
|
||||
@@ -78,6 +74,27 @@ public abstract class TitleScreenMixin {
|
||||
} else if (child instanceof PlainTextButton) {
|
||||
// Copyright Mojang cliquable en bas.
|
||||
toRemove.add(child);
|
||||
} else if (child instanceof Button button) {
|
||||
String msg = button.getMessage().getString();
|
||||
if (msg.equals(solo)) {
|
||||
// Solo retiré complètement (plutôt que grisé).
|
||||
toRemove.add(button);
|
||||
} else if (msg.equals(multi)) {
|
||||
joinX = button.getX();
|
||||
joinY = button.getY();
|
||||
foundMulti = true;
|
||||
toRemove.add(button);
|
||||
} else if (msg.equals(realms)) {
|
||||
toRemove.add(button);
|
||||
} else if (msg.equals(options)) {
|
||||
// Remplacé plus bas par un bouton icône (engrenage).
|
||||
optionsButton = button;
|
||||
toRemove.add(button);
|
||||
} else if (msg.equals(quit)) {
|
||||
// Remplacé plus bas par un bouton icône (power).
|
||||
quitButton = button;
|
||||
toRemove.add(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GuiEventListener widget : toRemove) {
|
||||
@@ -86,17 +103,61 @@ public abstract class TitleScreenMixin {
|
||||
|
||||
Button join = Button.builder(Component.literal("Rejoindre SurviePote"), pressed -> {
|
||||
ServerData data = new ServerData("SurviePote", SERVER_HOST, ServerData.Type.OTHER);
|
||||
ConnectScreen.startConnecting(self, minecraft, ServerAddress.parseString(SERVER_HOST), data, false,
|
||||
new TransferState(Map.of(), Map.of(), false));
|
||||
// TransferState null = connexion normale. En passer un déclenche une connexion
|
||||
// de type "transfert", que le serveur refuse ("This server does not accept transfers").
|
||||
ConnectScreen.startConnecting(self, minecraft, ServerAddress.parseString(SERVER_HOST), data, false, null);
|
||||
}).bounds(joinX, joinY, 200, 20).build();
|
||||
((ScreenAccessor) self).surviepote$getRenderables().add(join);
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
List rawChildren = (List) self.children();
|
||||
rawChildren.add(join);
|
||||
|
||||
// Bouton Discord (logo pixel art + texte) juste sous "Rejoindre SurviePote".
|
||||
DiscordButton discord = new DiscordButton(joinX, joinY + 24, 200, 20, "Join Discord", () ->
|
||||
Blaze3D.openUri(URI.create(DISCORD_URL)));
|
||||
((ScreenAccessor) self).surviepote$getRenderables().add(discord);
|
||||
rawChildren.add(discord);
|
||||
|
||||
// Options / Quit Game : boutons icônes (engrenage / power) sans texte.
|
||||
// On garde l'action du bouton vanilla d'origine.
|
||||
if (optionsButton != null) {
|
||||
final Button vanillaOptions = optionsButton;
|
||||
Button.OnPress press = ((ButtonAccessor) vanillaOptions).surviepote$getOnPress();
|
||||
IconButton icon = new IconButton(
|
||||
optionsButton.getX() + optionsButton.getWidth() / 2 - ICON_SIZE / 2,
|
||||
optionsButton.getY(), ICON_SIZE, ICON_SIZE,
|
||||
Component.literal("Options"),
|
||||
IconButton.OPTIONS_TEXTURE, OPTIONS_TEXTURE_PATH,
|
||||
() -> press.onPress(vanillaOptions));
|
||||
((ScreenAccessor) self).surviepote$getRenderables().add(icon);
|
||||
rawChildren.add(icon);
|
||||
}
|
||||
if (quitButton != null) {
|
||||
final Button vanillaQuit = quitButton;
|
||||
Button.OnPress press = ((ButtonAccessor) vanillaQuit).surviepote$getOnPress();
|
||||
IconButton icon = new IconButton(
|
||||
quitButton.getX() + quitButton.getWidth() / 2 - ICON_SIZE / 2,
|
||||
quitButton.getY(), ICON_SIZE, ICON_SIZE,
|
||||
Component.literal("Quit Game"),
|
||||
IconButton.QUIT_TEXTURE, QUIT_TEXTURE_PATH,
|
||||
() -> press.onPress(vanillaQuit));
|
||||
((ScreenAccessor) self).surviepote$getRenderables().add(icon);
|
||||
rawChildren.add(icon);
|
||||
}
|
||||
|
||||
// Panneau de statut du serveur en haut à gauche (en ligne / joueurs / ping).
|
||||
ServerStatusWidget status = new ServerStatusWidget(4, 4, 120, 24);
|
||||
((ScreenAccessor) self).surviepote$getRenderables().add(status);
|
||||
rawChildren.add(status);
|
||||
|
||||
if (join instanceof AbstractWidget w) {
|
||||
w.visible = true;
|
||||
w.active = true;
|
||||
}
|
||||
if (discord instanceof AbstractWidget w) {
|
||||
w.visible = true;
|
||||
w.active = true;
|
||||
}
|
||||
if (!foundMulti) {
|
||||
NoSingleplayer.LOGGER.warn("[SurviePote] Bouton Multi introuvable, Join placé par défaut.");
|
||||
}
|
||||
|
||||
+85
-5
@@ -1,11 +1,16 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import com.mojang.realmsclient.gui.screens.RealmsNotificationsScreen;
|
||||
import fr.surviepote.nosingleplayer.ModTextures;
|
||||
import fr.surviepote.nosingleplayer.NoSingleplayer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.components.LogoRenderer;
|
||||
import net.minecraft.client.gui.components.SplashRenderer;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@@ -19,23 +24,98 @@ public abstract class TitleScreenRenderMixin {
|
||||
private static final Identifier SURVIEPOTE_BACKGROUND = Identifier.fromNamespaceAndPath(
|
||||
"nosingleplayer", "textures/gui/background.png");
|
||||
|
||||
// Taille réelle de background.png (fond plein écran étiré).
|
||||
// Le PNG est lu directement dans le jar du mod (voir surviepote$loadBackground).
|
||||
@Unique
|
||||
private static final int BACKGROUND_TEX_W = 3840;
|
||||
private static final String SURVIEPOTE_BACKGROUND_RESOURCE =
|
||||
"/assets/nosingleplayer/textures/gui/background.png";
|
||||
|
||||
@Unique
|
||||
private static final int BACKGROUND_TEX_H = 2400;
|
||||
private static DynamicTexture surviepote$background;
|
||||
|
||||
@Unique
|
||||
private static int surviepote$backgroundWidth;
|
||||
|
||||
@Unique
|
||||
private static int surviepote$backgroundHeight;
|
||||
|
||||
// Pas de panorama dynamique : fond statique custom à la place.
|
||||
// Note : extractPanorama est déclarée dans Screen (héritée), on redirige donc
|
||||
// l'APPEL dans TitleScreen plutôt que la méthode elle-même.
|
||||
// Le fond couvre l'écran sans déformation : recadrage centré type "cover".
|
||||
@Redirect(method = "extractRenderState",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/screens/TitleScreen;extractPanorama(Lnet/minecraft/client/gui/GuiGraphicsExtractor;F)V"))
|
||||
private void surviepote$customBackground(TitleScreen self, GuiGraphicsExtractor extractor, float partialTick) {
|
||||
int w = Minecraft.getInstance().getWindow().getGuiScaledWidth();
|
||||
int h = Minecraft.getInstance().getWindow().getGuiScaledHeight();
|
||||
extractor.blit(RenderPipelines.GUI_TEXTURED, SURVIEPOTE_BACKGROUND, 0, 0, 0.0F, 0.0F,
|
||||
w, h, BACKGROUND_TEX_W, BACKGROUND_TEX_H, -1);
|
||||
|
||||
surviepote$loadBackground();
|
||||
if (surviepote$background == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float screenAspect = (float) w / (float) h;
|
||||
float texAspect = (float) surviepote$backgroundWidth / (float) surviepote$backgroundHeight;
|
||||
|
||||
int u, v, srcW, srcH;
|
||||
if (screenAspect > texAspect) {
|
||||
// Écran plus large que l'image : on rogne en haut/bas.
|
||||
srcW = surviepote$backgroundWidth;
|
||||
srcH = Math.round(surviepote$backgroundWidth / screenAspect);
|
||||
u = 0;
|
||||
v = (surviepote$backgroundHeight - srcH) / 2;
|
||||
} else {
|
||||
// Écran plus haut que l'image : on rogne à gauche/droite.
|
||||
srcH = surviepote$backgroundHeight;
|
||||
srcW = Math.round(surviepote$backgroundHeight * screenAspect);
|
||||
u = (surviepote$backgroundWidth - srcW) / 2;
|
||||
v = 0;
|
||||
}
|
||||
|
||||
// Surcharge complète : (u, v) = origine dans la texture, (w, h) = taille à
|
||||
// l'écran, (srcW, srcH) = taille de la région source dans la texture.
|
||||
// L'overload court réutilise w/h comme région source (=> zoom sur un coin).
|
||||
extractor.blit(RenderPipelines.GUI_TEXTURED, SURVIEPOTE_BACKGROUND,
|
||||
0, 0, (float) u, (float) v,
|
||||
w, h, srcW, srcH,
|
||||
surviepote$backgroundWidth, surviepote$backgroundHeight, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* MC 26.3 n'expose plus les ressources des mods au ResourceManager : les packs
|
||||
* ne sont lus que depuis des dossiers, pas depuis les jars du classpath. Une
|
||||
* texture référencée par son Identifier est donc introuvable (placeholder
|
||||
* magenta). Le PNG est donc lu dans le jar du mod (voir ModTextures).
|
||||
*/
|
||||
@Unique
|
||||
private static void surviepote$loadBackground() {
|
||||
DynamicTexture texture = ModTextures.get(SURVIEPOTE_BACKGROUND, SURVIEPOTE_BACKGROUND_RESOURCE,
|
||||
"SurviePote Background", true);
|
||||
if (texture == null) {
|
||||
surviepote$background = null;
|
||||
return;
|
||||
}
|
||||
if (surviepote$background != texture) {
|
||||
surviepote$background = texture;
|
||||
surviepote$backgroundWidth = texture.getPixels().getWidth();
|
||||
surviepote$backgroundHeight = texture.getPixels().getHeight();
|
||||
NoSingleplayer.LOGGER.info("[SurviePote] Fond chargé : {}x{}",
|
||||
surviepote$backgroundWidth, surviepote$backgroundHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Pas de texte de version ("26.3 mc") en bas à gauche.
|
||||
@Redirect(method = "extractRenderState",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;text(Lnet/minecraft/client/gui/Font;Ljava/lang/String;III)V"))
|
||||
private void surviepote$skipVersionText(GuiGraphicsExtractor extractor, Font font, String text, int x, int y, int color) {
|
||||
}
|
||||
|
||||
// Pas de texte de splash (message rotatif) sur le menu principal.
|
||||
@Redirect(method = "extractRenderState",
|
||||
at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/components/SplashRenderer;extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;ILnet/minecraft/client/gui/Font;F)V"))
|
||||
private void surviepote$skipSplash(SplashRenderer splash, GuiGraphicsExtractor extractor, int width, Font font, float fade) {
|
||||
}
|
||||
|
||||
// Pas de logo Minecraft.
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package fr.surviepote.nosingleplayer.mixin;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(TitleScreen.class)
|
||||
public class TitleScreenTextMixin {
|
||||
|
||||
@Redirect(
|
||||
method = "extractRenderState",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;text(Lnet/minecraft/client/gui/Font;Ljava/lang/String;III)V"
|
||||
)
|
||||
)
|
||||
private void surviepote$versionLine(GuiGraphicsExtractor extractor, Font font, String text, int x, int y, int color) {
|
||||
if (text.contains("Copyright") || text.contains("Mojang")) {
|
||||
return;
|
||||
}
|
||||
extractor.text(font, "26.3 mc", x, y, color);
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 629 KiB After Width: | Height: | Size: 1.9 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 904 B |
Binary file not shown.
|
After Width: | Height: | Size: 645 B |
Binary file not shown.
|
After Width: | Height: | Size: 704 B |
@@ -5,8 +5,10 @@
|
||||
"client": [
|
||||
"TitleScreenMixin",
|
||||
"TitleScreenRenderMixin",
|
||||
"TitleScreenTextMixin",
|
||||
"MinecraftTitleMixin",
|
||||
"ButtonAccessor",
|
||||
"RealmsNotificationsMixin",
|
||||
"PauseScreenMixin",
|
||||
"ScreenAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -9,7 +9,9 @@
|
||||
- À faire : le retirer aussi de Prism (`~/.local/share/PrismLauncher/instances/26.3/minecraft/mods/`),
|
||||
sinon le client crashera pareil. Réintégrer quand une 5.2.2+ sortira.
|
||||
|
||||
## ImmersiveOres — voir server/mods-client-only/
|
||||
- `ImmersiveOres-26.3-0.1.jar` : mixin client (`ToolOverlayRenderer` → `LevelRenderer`) dans la
|
||||
config commune → crash serveur dédié. Gardé côté CLIENT (atlas required) pour les blocs/items,
|
||||
exclu du serveur. À signaler à l'auteur.
|
||||
## ImmersiveOres — RETIRÉ (mod cassé, 19/10/2026)
|
||||
- `ImmersiveOres-26.3-0.1.jar` : `environment: *` mais mixin client (`ToolOverlayRenderer` → `LevelRenderer`)
|
||||
dans la config commune → crash serveur dédié. Mod jugé cassé → retiré partout : serveur,
|
||||
`atlas/.../fabricmods/required/` (clients) et config `config/immersiveores/`.
|
||||
- À faire : le retirer aussi de Prism (`~/.local/share/PrismLauncher/instances/26.3/minecraft/mods/`),
|
||||
sinon le client plantera pareil. À signaler à l'auteur.
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
+49
-9
@@ -12,11 +12,20 @@ Serveur **Minecraft 26.3 + Fabric Loader 0.20.0**, pré-configuré en « défaut
|
||||
|
||||
```bash
|
||||
cd server
|
||||
echo "eula=true" > eula.txt
|
||||
./start.sh
|
||||
echo "eula=true" > eula.txt # déjà présent (eula=false) : à passer à true
|
||||
./start.sh # installe le runtime automatiquement si absent, puis démarre
|
||||
```
|
||||
|
||||
(Sous Windows : `start.bat`.)
|
||||
(Sous Windows : `start.bat`, même comportement.)
|
||||
|
||||
Le runtime Fabric (jars vanilla/Fabric, `libraries/`, `versions/`, `.fabric/`) n'est **pas
|
||||
versionné** (voir `.gitignore`) : il est (re)généré par `setup.sh`, appelé automatiquement par
|
||||
`start.sh` s'il manque, ou manuellement :
|
||||
|
||||
```bash
|
||||
cd server
|
||||
nix-shell .. --run 'bash server/setup.sh' # ou : ./setup.sh (avec Java dans le PATH)
|
||||
```
|
||||
|
||||
Au premier démarrage, le serveur génère `world/`, `config/`, `logs/`, etc.
|
||||
Pour régénérer le monde (ex. changer de `level-type`), stoppe le serveur et supprime `world/`.
|
||||
@@ -25,18 +34,20 @@ Pour régénérer le monde (ex. changer de `level-type`), stoppe le serveur et s
|
||||
|
||||
| Fichier | Rôle |
|
||||
| --- | --- |
|
||||
| `fabric-server-launch.jar` | Lanceur Fabric (généré par fabric-installer 1.1.2) |
|
||||
| `server.jar` | Serveur Minecraft 26.3 vanilla (téléchargé par l'installeur) |
|
||||
| `fabric-installer.jar` | Installeur (pour réinstaller/réparer) |
|
||||
| `mods/` | 31 mods serveur (copie physique) — voir `mods/SOURCE.md` |
|
||||
| `config/` | Configs générées au premier lancement (+ customs à déposer ici) |
|
||||
| `setup.sh` / `setup.bat` | Installe/repare le runtime Fabric (télécharge vanilla + lanceur Fabric) |
|
||||
| `fabric-server-launch.jar` | Lanceur Fabric (généré par fabric-installer 1.1.2 — non versionné) |
|
||||
| `server.jar` | Serveur Minecraft 26.3 vanilla (téléchargé par l'installeur — non versionné) |
|
||||
| `fabric-installer.jar` | Installeur (pour réinstaller/réparer — non versionné) |
|
||||
| `mods/` | 46 mods serveur (copie physique) — voir `mods/SOURCE.md` |
|
||||
| `config/` | Configs générées au premier lancement (non versionnées) |
|
||||
| `server.properties` | Config plug-and-play (survie, normal, 20 joueurs) |
|
||||
| `eula.txt` | Passer à `eula=true` avant de lancer |
|
||||
| `start.sh` / `start.bat` | Scripts de lancement (4 Go RAM, `nogui`) |
|
||||
| `ecosystem.config.cjs` | Config PM2 (démarre le serveur dans le nix-shell, Java 25) |
|
||||
|
||||
## Mods
|
||||
|
||||
- **Inclus (31)** : gameplay/contenu (Better Combat, BOP, BCLib/Better End, Waystones, Backpacks, Drawers, VeinMiner, Tree Harvester, Jade, JEI, etc.). Détail dans `mods/SOURCE.md`.
|
||||
- **Inclus (46)** : gameplay/contenu (Better Combat, BOP, BCLib/Better End, Waystones, Backpacks, Drawers, VeinMiner, Tree Harvester, Jade, JEI, etc.). Détail dans `mods/SOURCE.md`.
|
||||
- **Exclus (15, client-only)** : sodium, iris, continuity, dynamic-fps, xaerominimap, xaeroworldmap, skinlayers3d, waveycapes, capes, chat_heads, morechathistory, fast-ip-ping, modmenu, appleskin, vanillarice. À garder côté **client** uniquement.
|
||||
- Resourcepacks/shaders : jamais côté serveur.
|
||||
|
||||
@@ -49,4 +60,33 @@ Pour régénérer le monde (ex. changer de `level-type`), stoppe le serveur et s
|
||||
## Réseau
|
||||
|
||||
- `server-port=25565`, `online-mode=true`, `white-list=false`.
|
||||
- `server-ip=10.7.0.2` : le serveur n'écoute **que** sur l'interface portant cette IP (VPN).
|
||||
Laisser vide = écouter sur toutes les interfaces (`0.0.0.0`).
|
||||
- Ouvre le port 25565/TCP (box/pare-feu) et pointe `mcpv.ihorizon.org` vers l'IP publique.
|
||||
|
||||
## PM2 (daemon)
|
||||
|
||||
`ecosystem.config.cjs` lance le serveur **dans le nix-shell du repo** (`shell.nix` → `jdk25`),
|
||||
sans dépendre du Java du système. Les chemins sont résolus depuis le fichier (`__dirname`) :
|
||||
`server/` et la racine du repo.
|
||||
|
||||
```bash
|
||||
cd /srv/mc/server
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 logs surviepote-mc
|
||||
pm2 restart surviepote-mc
|
||||
pm2 stop surviepote-mc
|
||||
|
||||
pm2 save # mémorise la liste des process
|
||||
sudo pm2 startup systemd -u anais --hp /home/anais # relance au boot
|
||||
```
|
||||
|
||||
Équivalent manuel de la commande lancée par PM2 :
|
||||
|
||||
```bash
|
||||
cd /srv/mc/server
|
||||
nix-shell /srv/mc --run "bash /srv/mc/server/start.sh"
|
||||
```
|
||||
|
||||
> PM2 ne relaie pas la console interactive : utilise `pm2 logs` pour la sortie et
|
||||
> `pm2 sendSignal`/`pm2 stop` (SIGINT → sauvegarde propre via le shutdown hook Java).
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,24 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Ores",
|
||||
"blocks": [
|
||||
"#c:ores"
|
||||
],
|
||||
"tools": [
|
||||
"#minecraft:pickaxes"
|
||||
],
|
||||
"override": {
|
||||
"cooldown": null,
|
||||
"mustSneak": null,
|
||||
"delay": null,
|
||||
"maxChain": null,
|
||||
"needCorrectTool": null,
|
||||
"searchRadius": null,
|
||||
"permissionRestricted": null,
|
||||
"decreaseDurability": null,
|
||||
"hungerPerBlock": null,
|
||||
"miningSpeedModifier": null,
|
||||
"separateGroupMining": null
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"cooldown": 20,
|
||||
"mustSneak": false,
|
||||
"delay": 0,
|
||||
"maxChain": 100,
|
||||
"needCorrectTool": true,
|
||||
"searchRadius": 1,
|
||||
"permissionRestricted": false,
|
||||
"mergeItemDrops": false,
|
||||
"autoUpdate": false,
|
||||
"decreaseDurability": true,
|
||||
"hungerPerBlock": 0.0,
|
||||
"miningSpeedModifier": 0.0,
|
||||
"separateGroupMining": false,
|
||||
"dontDisplayUpdatesAndIWillNotAskForSupportBeforeUpdatingISwear": false,
|
||||
"debug": false,
|
||||
"client": {
|
||||
"allow": true,
|
||||
"require": false,
|
||||
"translucentBlockHighlight": true,
|
||||
"allBlocks": false,
|
||||
"overrides": {
|
||||
"cooldown": null,
|
||||
"mustSneak": null,
|
||||
"delay": null,
|
||||
"maxChain": null,
|
||||
"needCorrectTool": null,
|
||||
"searchRadius": null,
|
||||
"permissionRestricted": null,
|
||||
"decreaseDurability": null,
|
||||
"hungerPerBlock": null,
|
||||
"miningSpeedModifier": null,
|
||||
"separateGroupMining": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"create_version": "26.301.2",
|
||||
"rendering": {
|
||||
"custom_fog_rendering": true,
|
||||
"nether_thick_fog": true,
|
||||
"fog_density": 1.0
|
||||
},
|
||||
"ui": {
|
||||
"survives_on_hint": true
|
||||
},
|
||||
"modify_version": "26.301.2"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"create_version": "26.301.2",
|
||||
"patches": {
|
||||
"apply_patches": true
|
||||
},
|
||||
"modify_version": "26.301.2"
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"blacklist_item_id_regex": "pickaxe",
|
||||
"fallback_compatibility": [
|
||||
{
|
||||
"item_id_regex": "claymore|great_sword|greatsword",
|
||||
"weapon_attributes": "bettercombat:claymore"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "great_hammer|greathammer|war_hammer|warhammer|maul",
|
||||
"weapon_attributes": "bettercombat:hammer"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "double_axe|doubleaxe|war_axe|waraxe|great_axe|greataxe",
|
||||
"weapon_attributes": "bettercombat:double_axe"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "scythe",
|
||||
"weapon_attributes": "bettercombat:scythe"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "halberd",
|
||||
"weapon_attributes": "bettercombat:halberd"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "glaive",
|
||||
"weapon_attributes": "bettercombat:glaive"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "spear",
|
||||
"weapon_attributes": "bettercombat:spear"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "lance",
|
||||
"weapon_attributes": "bettercombat:lance"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "anchor",
|
||||
"weapon_attributes": "bettercombat:anchor"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "battlestaff|battle_staff",
|
||||
"weapon_attributes": "bettercombat:battlestaff"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "claw",
|
||||
"weapon_attributes": "bettercombat:claw"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "fist|gauntlet",
|
||||
"weapon_attributes": "bettercombat:fist"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "trident|javelin|impaled",
|
||||
"weapon_attributes": "bettercombat:trident"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "katana",
|
||||
"weapon_attributes": "bettercombat:katana"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "rapier",
|
||||
"weapon_attributes": "bettercombat:rapier"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "sickle",
|
||||
"weapon_attributes": "bettercombat:sickle"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "soul_knife",
|
||||
"weapon_attributes": "bettercombat:soul_knife"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "dagger|knife",
|
||||
"weapon_attributes": "bettercombat:dagger"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "staff|wand|sceptre|stave|rod",
|
||||
"weapon_attributes": "bettercombat:wand"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "mace|hammer|flail",
|
||||
"weapon_attributes": "bettercombat:mace"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "axe",
|
||||
"weapon_attributes": "bettercombat:axe"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "coral_blade",
|
||||
"weapon_attributes": "bettercombat:coral_blade"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "twin_blade|twinblade",
|
||||
"weapon_attributes": "bettercombat:twin_blade"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "cutlass|scimitar|machete",
|
||||
"weapon_attributes": "bettercombat:cutlass"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "sword|blade",
|
||||
"weapon_attributes": "bettercombat:sword"
|
||||
}
|
||||
],
|
||||
"ranged_weapons": [
|
||||
{
|
||||
"item_id_regex": "two_handed_crossbow",
|
||||
"weapon_attributes": "bettercombat:crossbow_two_handed_heavy"
|
||||
},
|
||||
{
|
||||
"item_id_regex": "two_handed_bow",
|
||||
"weapon_attributes": "bettercombat:bow_two_handed_heavy"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
{
|
||||
/* Upswing (aka windup) is the first phase of the attack (between clicking and performing the damage).
|
||||
Typical duration of upswing is `weapon cooldown * 0.5`. (Weapon specific upswing values can be defined in weapon attributes)
|
||||
This config allows you to change upswing duration.
|
||||
Example values:
|
||||
- `0.5` (default, fast paced attack initiation) upswing typically lasts 25% of the attack cooldown
|
||||
- `1.0` (classic setting, realistic attack initiation) upswing typically lasts 50% of the attack cooldown
|
||||
*/
|
||||
"upswing_multiplier": 0.5,
|
||||
// Bypass damage receive throttling of LivingEntity from player attacks.
|
||||
"allow_fast_attacks": true,
|
||||
// Allows client-side target search and server-side attack request execution against currently mounted entity of the player
|
||||
"allow_attacking_mount": false,
|
||||
// The minimum number of ticks between two attacks
|
||||
"attack_interval_cap": 2,
|
||||
/* Blacklist for entities that are acting as vehicle but should not be treated as protected mounts.
|
||||
Classical example is an alexsmobs:crocodile attempting a death spin.
|
||||
(Note all hostile mobs hittable by default, this config is to fix faulty mobs)
|
||||
*/
|
||||
"hostile_player_vehicles": [
|
||||
"alexsmobs:crocodile"
|
||||
],
|
||||
// Allows vanilla sweeping mechanic to work and Sweeping Edge enchantment
|
||||
"allow_vanilla_sweeping": false,
|
||||
// Allows new sweeping mechanic (by Better Combat) to work, including Sweeping Edge enchantment
|
||||
"allow_reworked_sweeping": true,
|
||||
/* The more additional targets a weapon swing hits, the weaker it will get.
|
||||
Entities struck (+1) in a swing more than this, won't get weakened any further.
|
||||
*/
|
||||
"reworked_sweeping_extra_target_count": 4,
|
||||
/* Determines how weak the attack becomes when striking `reworked_sweeping_extra_target_count + 1` targets.
|
||||
Example values:
|
||||
- `0.5` -50% damage
|
||||
*/
|
||||
"reworked_sweeping_maximum_damage_penalty": 0.5,
|
||||
"reworked_sweeping_plays_sound": true,
|
||||
"reworked_sweeping_emits_particles": true,
|
||||
"reworked_sweeping_sound_and_particles_only_for_swords": true,
|
||||
// Allows client-side target search to ignore obstacles. WARNING! Setting this to `false` significantly increases the load on clients.
|
||||
"allow_attacking_thru_walls": false,
|
||||
// Applies movement speed multiplier while attacking. (Min: 0, Max: 1). Use `0` for a full stop while attacking. Use `1` for no movement speed penalty
|
||||
"movement_speed_while_attacking": 0.5,
|
||||
// Determines if applying the movement speed multiplier while attacking is done smoothly or instantly
|
||||
"movement_speed_applied_smoothly": true,
|
||||
// Determines whether or not to apply movement speed reduction while attacking mounted
|
||||
"movement_speed_effected_while_mounting": false,
|
||||
// Attacks faster than a vanilla threshold will do smaller knockback, proportionally.
|
||||
"knockback_reduced_for_fast_attacks": true,
|
||||
// Attack cooldown in ticks, below which knockback will be reduced
|
||||
"knockback_reduction_threshold": 12.5,
|
||||
// Knockback reduction curve. Options: LINEAR, SQUARE, HALF_SQUARE
|
||||
"knockback_reduction_curve": "HALF_SQUARE",
|
||||
// Combo is reset after idling `combo_reset_rate * weapon_cooldown`
|
||||
"combo_reset_rate": 3.0,
|
||||
// Multiplier for `attack_range`, during target lookup on both sides. Large sized entities may be colliding with weapon hitbox, but center of entities can have bigger distance than `attack_range`
|
||||
"target_search_range_multiplier": 2.0,
|
||||
// Determines if the server should validate the target range. (If the target is within the range of the weapon)
|
||||
"server_target_range_validation": false,
|
||||
/* Determines how much the scale (size) of the player affects `attack_range`. (Also applies to scales from Pehkui)
|
||||
Example values:
|
||||
- `0` scale has no effect on attack range
|
||||
- `0.5` attack range is affected by scale half as much
|
||||
- `1` (default) attack range changes proportionally with scale
|
||||
- `2` attack range is affected by scale twice as much
|
||||
*/
|
||||
"entity_scale_attack_range_influence": 1.0,
|
||||
// Total multiplier, (examples: +30% = 1.3, -30% = 0.7)
|
||||
"dual_wielding_attack_speed_multiplier": 1.2000000476837158,
|
||||
// Total multiplier, (examples: +30% = 1.3, -30% = 0.7)
|
||||
"dual_wielding_main_hand_damage_multiplier": 1.0,
|
||||
// Total multiplier, (examples: +30% = 1.3, -30% = 0.7)
|
||||
"dual_wielding_off_hand_damage_multiplier": 1.0,
|
||||
/* Relations determine when players' undirected weapon swings (cleaves) will hurt another entity (target).
|
||||
- `FRIENDLY` - The target can never be damaged by the player.
|
||||
- `NEUTRAL` - The target can be damaged only if the player is directly looking at it.
|
||||
- `HOSTILE` - The target can be damaged if located within the weapon swing area.
|
||||
(NOTE: Vanilla sweeping can still hit targets, if not disabled via `allow_sweeping`)
|
||||
|
||||
The various relation related configs are being checked in the following order:
|
||||
- `player_relations`
|
||||
- `player_relation_to_passives`
|
||||
- `player_relation_to_hostiles`
|
||||
- `player_relation_to_other`
|
||||
(The first relation to be found for the target will be applied.)
|
||||
*/
|
||||
"player_relations": {
|
||||
"minecraft:player": "NEUTRAL",
|
||||
"minecraft:villager": "NEUTRAL",
|
||||
"minecraft:iron_golem": "NEUTRAL",
|
||||
"minecraft:copper_golem": "NEUTRAL",
|
||||
"guardvillagers:guard": "NEUTRAL"
|
||||
},
|
||||
"player_relation_tags": {
|
||||
"minecraft:undead": "HOSTILE"
|
||||
},
|
||||
// Relation to self, and self owned pets (tamed entities), changing this to `FRIENDLY` will make it impossible to hit pets with weapons
|
||||
"player_relation_to_self_and_pets": "NEUTRAL",
|
||||
// Relation to teammates (entities in the same team), changing this to `FRIENDLY` will automatically disable friendly fire for all teams
|
||||
"player_relation_to_teammates": "NEUTRAL",
|
||||
// Relation to unspecified entities those are instance of PassiveEntity(Yarn)
|
||||
"player_relation_to_passives": "HOSTILE",
|
||||
// Relation to unspecified entities those are instance of HostileEntity(Yarn)
|
||||
"player_relation_to_hostiles": "HOSTILE",
|
||||
// Fallback relation
|
||||
"player_relation_to_other": "HOSTILE",
|
||||
// Try to guess and apply a preset for items without weapon attributes data file
|
||||
"fallback_compatibility_enabled": true,
|
||||
// Allow printing the content of weapon attributes registry
|
||||
"weapon_registry_logging": false,
|
||||
// Compress the weapon attributes registry for transmission
|
||||
"weapon_registry_compression": true
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
{
|
||||
"trail_appearance": {
|
||||
"default_appearance": {
|
||||
"primary": {
|
||||
"color_rgba": 4294967193,
|
||||
"glows": false
|
||||
},
|
||||
"secondary": {
|
||||
"color_rgba": 2576980326,
|
||||
"glows": false
|
||||
}
|
||||
},
|
||||
"conditional": {
|
||||
"is_enchanted": {
|
||||
"primary": {
|
||||
"color_rgba": 1725562777,
|
||||
"glows": true
|
||||
},
|
||||
"secondary": {
|
||||
"color_rgba": 2582052684,
|
||||
"glows": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation_based": {
|
||||
"bettercombat:one_handed_slash_horizontal_right": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_slash_horizontal_left": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 180.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_uppercut_right": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 75.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_swipe_horizontal_right": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -0.1
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_slam": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.05,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 45.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -85.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_stab": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.13,
|
||||
"z_addition": 0.2,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_stab_mounted": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": 0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_punch": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.15,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:dual_handed_slash_cross": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.2,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -120.0
|
||||
},
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": -0.2,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -60.0
|
||||
}
|
||||
],
|
||||
"bettercombat:dual_handed_slash_uncross": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.2,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 240.0
|
||||
},
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": -0.2,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 300.0
|
||||
}
|
||||
],
|
||||
"bettercombat:dual_handed_stab": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.4,
|
||||
"y_addition": -0.3,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
},
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": -0.4,
|
||||
"y_addition": -0.3,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_stab_left": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_stab_right": [
|
||||
{
|
||||
"particle_type": "stab",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.15,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slash_horizontal_right": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -5.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slash_horizontal_left": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 180.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_slash_switch_blade_right": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 0.0
|
||||
}
|
||||
],
|
||||
"bettercombat:one_handed_slash_switch_blade_left": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 180.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_spin": [
|
||||
{
|
||||
"particle_type": "slash360",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 0.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": 180.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slash_vertical_right": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 45.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -80.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slash_vertical_left": [
|
||||
{
|
||||
"particle_type": "slash90",
|
||||
"x_addition": 0.0,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 45.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -100.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slam": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.1,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 45.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -86.0
|
||||
}
|
||||
],
|
||||
"bettercombat:two_handed_slam_heavy": [
|
||||
{
|
||||
"particle_type": "slash180",
|
||||
"x_addition": 0.1,
|
||||
"y_addition": -0.1,
|
||||
"z_addition": 0.0,
|
||||
"local_yaw": 45.0,
|
||||
"pitch_addition": 0.0,
|
||||
"roll_set": -86.0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"create_version": "26.301.2",
|
||||
"rendering": {
|
||||
"custom_sky": true,
|
||||
"sulfur_water_color": true,
|
||||
"blend_biome_music": true
|
||||
},
|
||||
"modify_version": "26.301.2"
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"create_version": "26.301.2",
|
||||
"structure": {
|
||||
"generate_central_island": true,
|
||||
"generate_obsidian_platform": true,
|
||||
"has_portal": true,
|
||||
"replace_portal": true,
|
||||
"has_pillars": true,
|
||||
"replace_pillars": true,
|
||||
"biome_size_caves": 32,
|
||||
"end_city_fail_chance": 1
|
||||
},
|
||||
"generator": {
|
||||
"use_new_generator": true,
|
||||
"layers": {
|
||||
"big_islands": {
|
||||
"distance": 300.0,
|
||||
"scale": 200.0,
|
||||
"average_height": 0.546875,
|
||||
"height_variation": 0.078125,
|
||||
"coverage": 0.5,
|
||||
"contains_central_island": false
|
||||
},
|
||||
"medium_islands": {
|
||||
"distance": 150.0,
|
||||
"scale": 100.0,
|
||||
"average_height": 0.546875,
|
||||
"height_variation": 0.15625,
|
||||
"coverage": 0.5,
|
||||
"contains_central_island": true
|
||||
},
|
||||
"small_islands": {
|
||||
"distance": 60.0,
|
||||
"scale": 50.0,
|
||||
"average_height": 0.546875,
|
||||
"height_variation": 0.234375,
|
||||
"coverage": 0.5,
|
||||
"contains_central_island": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"has_dragon_fights": true,
|
||||
"spawn": {
|
||||
"has_spawn": false,
|
||||
"point": {
|
||||
"x": 20,
|
||||
"y": 65,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
"give_guide_book": true
|
||||
},
|
||||
"cosmetic": {
|
||||
"change_chorus_plant": true
|
||||
},
|
||||
"modify_version": "26.301.2"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"portals": [
|
||||
{
|
||||
"dimension": "minecraft:overworld",
|
||||
"item": "betterend:eternal_crystal",
|
||||
"colorRed": 255,
|
||||
"colorGreen": 255,
|
||||
"colorBlue": 255
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"auroral_garden_enabled": true,
|
||||
"bayou_enabled": true,
|
||||
"bog_enabled": true,
|
||||
"cold_desert_enabled": true,
|
||||
"coniferous_forest_enabled": true,
|
||||
"crag_enabled": true,
|
||||
"crystalline_chasm_enabled": true,
|
||||
"dead_forest_enabled": true,
|
||||
"dryland_enabled": true,
|
||||
"dune_beach_enabled": true,
|
||||
"end_corruption_enabled": true,
|
||||
"end_flats_enabled": true,
|
||||
"end_reef_enabled": true,
|
||||
"end_wilds_enabled": true,
|
||||
"erupting_inferno_enabled": true,
|
||||
"field_enabled": true,
|
||||
"fir_clearing_enabled": true,
|
||||
"floodplain_enabled": true,
|
||||
"forested_field_enabled": true,
|
||||
"fungal_jungle_enabled": true,
|
||||
"glowing_grotto_enabled": true,
|
||||
"gravel_beach_enabled": true,
|
||||
"highland_enabled": true,
|
||||
"hot_springs_enabled": true,
|
||||
"jade_cliffs_enabled": true,
|
||||
"lavender_field_enabled": true,
|
||||
"lush_desert_enabled": true,
|
||||
"lush_savanna_enabled": true,
|
||||
"maple_woods_enabled": true,
|
||||
"marsh_enabled": true,
|
||||
"mediterranean_forest_enabled": true,
|
||||
"moor_enabled": true,
|
||||
"muskeg_enabled": true,
|
||||
"mystic_grove_enabled": true,
|
||||
"old_growth_dead_forest_enabled": true,
|
||||
"old_growth_woodland_enabled": true,
|
||||
"ominous_woods_enabled": true,
|
||||
"orchard_enabled": true,
|
||||
"origin_valley_enabled": true,
|
||||
"overgrown_greens_enabled": true,
|
||||
"pasture_enabled": true,
|
||||
"prairie_enabled": true,
|
||||
"pumpkin_patch_enabled": true,
|
||||
"rainforest_enabled": true,
|
||||
"redwood_forest_enabled": true,
|
||||
"rocky_rainforest_enabled": true,
|
||||
"rocky_shrubland_enabled": true,
|
||||
"scrubland_enabled": true,
|
||||
"seasonal_forest_enabled": true,
|
||||
"shrubland_enabled": true,
|
||||
"snowblossom_grove_enabled": true,
|
||||
"snowy_coniferous_forest_enabled": true,
|
||||
"snowy_fir_clearing_enabled": true,
|
||||
"snowy_maple_woods_enabled": true,
|
||||
"spider_nest_enabled": true,
|
||||
"subtropics_enabled": true,
|
||||
"tropics_enabled": true,
|
||||
"tundra_enabled": true,
|
||||
"undergrowth_enabled": true,
|
||||
"visceral_heap_enabled": true,
|
||||
"volcanic_plains_enabled": true,
|
||||
"volcano_enabled": true,
|
||||
"wasteland_enabled": true,
|
||||
"wasteland_steppe_enabled": true,
|
||||
"wetland_enabled": true,
|
||||
"wintry_origin_valley_enabled": true,
|
||||
"withered_abyss_enabled": true,
|
||||
"woodland_enabled": true
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
[nether]
|
||||
#The weighting of bop biome regions in the nether.
|
||||
bop_nether_region_weight = 13
|
||||
#The weighting of rare bop biome regions in the nether.
|
||||
bop_nether_rare_region_weight = 2
|
||||
[overworld]
|
||||
#The weighting of primary bop biome regions in the overworld.
|
||||
bop_primary_overworld_region_weight = 10
|
||||
#The weighting of rare bop biome regions in the overworld.
|
||||
bop_overworld_rare_region_weight = 2
|
||||
#The weighting of secondary bop biome regions in the overworld.
|
||||
bop_secondary_overworld_region_weight = 8
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"cooldown": 5,
|
||||
"maxHomes": 5
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"jade:loot_table": null,
|
||||
"jade:object_name": null,
|
||||
"minecraft:animal_owner": null,
|
||||
"minecraft:beehive": null,
|
||||
"minecraft:brewing_stand": null,
|
||||
"minecraft:campfire": null,
|
||||
"minecraft:command_block": null,
|
||||
"minecraft:energy_storage": null,
|
||||
"minecraft:energy_storage.default": null,
|
||||
"minecraft:entity_health": null,
|
||||
"minecraft:fluid_storage": null,
|
||||
"minecraft:fluid_storage.default": null,
|
||||
"minecraft:furnace": null,
|
||||
"minecraft:hopper_lock": null,
|
||||
"minecraft:item_storage": null,
|
||||
"minecraft:item_storage.default": null,
|
||||
"minecraft:jukebox": null,
|
||||
"minecraft:lectern": null,
|
||||
"minecraft:mob_breeding": null,
|
||||
"minecraft:mob_growth": null,
|
||||
"minecraft:mob_spawner.cooldown": null,
|
||||
"minecraft:next_entity_drop": null,
|
||||
"minecraft:pet_armor": null,
|
||||
"minecraft:potion_effects": null,
|
||||
"minecraft:progress": null,
|
||||
"minecraft:redstone": null,
|
||||
"minecraft:shelf": null,
|
||||
"minecraft:villager_restock": null,
|
||||
"minecraft:waxed": null,
|
||||
"minecraft:zombie_villager": null
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# Name: Server Cheat Permissions
|
||||
# Description: Choose who can use JEI cheat mode on this server.
|
||||
[permissions]
|
||||
# Name: Allow Cheat Mode for Operators
|
||||
# Description: Allow players with operator status (/op) to use JEI cheat mode.
|
||||
# Valid Values: [true, false]
|
||||
# Default Value: true
|
||||
enableCheatModeForOp = true
|
||||
|
||||
# Name: Allow Cheat Mode in Creative Mode
|
||||
# Description: Allow players in Creative Mode to use JEI cheat mode.
|
||||
# Valid Values: [true, false]
|
||||
# Default Value: true
|
||||
enableCheatModeForCreative = true
|
||||
|
||||
# Name: Allow Cheat Mode with Give Permission
|
||||
# Description: Allow players who can use the /give command to use JEI cheat mode.
|
||||
# Valid Values: [true, false]
|
||||
# Default Value: false
|
||||
enableCheatModeForGive = false
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"common": {
|
||||
"allowTeleportComment": "Allows a player to teleport to a located biome when in creative mode, opped, or in cheat mode.",
|
||||
"allowTeleport": true,
|
||||
"maxNextSearchesComment": "The maximum number of times a player can search for the next instance of a located biome. Set to 0 to disable searching for additional biome instances and make the compass always locate the nearest biome.",
|
||||
"maxNextSearches": 25,
|
||||
"displayCoordinatesComment": "Allows players to view the precise coordinates and distance of a located structure on the HUD, rather than relying on the direction the compass is pointing.",
|
||||
"displayCoordinates": true,
|
||||
"maxSamplesComment": "The maximum number of samples to be taken when searching for a biome.",
|
||||
"maxSamples": 50000,
|
||||
"radiusModifierComment": "biomeSize * radiusModifier = maxSearchRadius. Raising this value will increase search accuracy but will potentially make the process more resource .",
|
||||
"radiusModifier": 2500,
|
||||
"sampleSpaceModifierComment": "biomeSize * sampleSpaceModifier = sampleSpace. Lowering this value will increase search accuracy but will make the process more resource intensive.",
|
||||
"sampleSpaceModifier": 16,
|
||||
"biomeBlacklistComment": "A list of biomes that the compass will not be able to search for, specified by resource location. The wildcard character * can be used to match any number of characters, and ? can be used to match one character. Ex (ignore backslashes): [\"minecraft:savanna\", \"minecraft:desert\", \"minecraft:*ocean*\"]",
|
||||
"biomeBlacklist": [],
|
||||
"defaultXpLevelsComment": "The default number of XP levels consumed when searching for a biome. Individual biomes can be configured via xpLevelOverrides. Max of 3 levels.",
|
||||
"defaultXpLevels": 0,
|
||||
"perBiomeXpLevelsComment": "A map of per-biome XP level costs that override defaultXpLevels. Biomes not listed here use defaultXpLevels. Max of 3 levels. The wildcard character * can be used to match any number of characters, and ? can be used to match one character. Ex: {\"minecraft:deep_dark\":3, \"minecraft:end*\":2, \"minecraft:*caves\":3}",
|
||||
"perBiomeXpLevels": {},
|
||||
"compassDurabilityComment": "The number of successful biome searches before the compass breaks and must be repaired. Set to 0 to disable durability.",
|
||||
"compassDurability": 0
|
||||
},
|
||||
"client": {
|
||||
"displayWithChatOpenComment": "Displays Nature's Compass information even while chat is open.",
|
||||
"displayWithChatOpen": true,
|
||||
"fixBiomeNamesComment": "Fixes biome names by adding missing spaces. Ex: ForestHills becomes Forest Hills",
|
||||
"fixBiomeNames": true,
|
||||
"overlayLineOffsetComment": "The line offset for information rendered on the HUD.",
|
||||
"overlayLineOffset": 1,
|
||||
"overlaySideComment": "The side for information rendered on the HUD. Ex: LEFT, RIGHT",
|
||||
"overlaySide": "LEFT"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#Packet Fixer config file.
|
||||
#Default values (minecraft default): nbtMaxSize 2097152, packetSize 1048576, decoderSize 8388608 and varInt21Size 3.
|
||||
#Max values are 2147483647 for packetSize/decoderSize/varInt21 and 9223372036854775807 for nbtMaxSize.
|
||||
#Sun Oct 18 22:04:32 CEST 2026
|
||||
allSizesUnlimited=true
|
||||
chunkPacketData=2097152
|
||||
decoderSize=8388608
|
||||
forceUnlimitedNbtEnabled=false
|
||||
keepAliveTimeout=120
|
||||
loginTimeout=120
|
||||
nbtMaxSize=2097152
|
||||
packetSize=1048576
|
||||
playerIdleTimeout=0
|
||||
readTimeout=120
|
||||
stringSize=32767
|
||||
varInt=5
|
||||
varInt21=3
|
||||
varLong=10
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"debug": false,
|
||||
"version": "1.0.0",
|
||||
"listMode": "DISABLED",
|
||||
"rateLimit": {
|
||||
"enable": false,
|
||||
"tokenLimit": 20,
|
||||
"tokenIncrement": 1,
|
||||
"tokenIncrementInterval": "1sec",
|
||||
"permissionBypass": "patpat.ratelimit.bypass"
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"//": "These settings are easier to change in-game! Mod List -> Collective -> Config",
|
||||
"disabledSerilumMods": {
|
||||
"//": "The Serilum mods that are turned off, picked from the ones installed. A mod's own 'Enable' setting follows this list, and the other way round. Takes effect after a restart.",
|
||||
"value": ""
|
||||
},
|
||||
"enableUpdateChecker": {
|
||||
"//": "Whether Collective should show a message in the console if a dependent mod has an update available. Update checks are optional and async.",
|
||||
"value": true
|
||||
},
|
||||
"transferItemsBetweenReplacedEntities": {
|
||||
"//": "When enabled, transfer the held items and armour from replaced entities by any of the Entity Spawn mods which depend on Collective.",
|
||||
"value": true
|
||||
},
|
||||
"loopsAmountUsedToGetAllEntityDrops": {
|
||||
"//": "The amount of times Collective loops through possible mob drops to get them all procedurally. Drops are only generated when a dependent mod uses them. Lowering this can increase world load time but decrease accuracy. min: 1, max: 500",
|
||||
"value": 100
|
||||
},
|
||||
"findABlockCheckAroundEntitiesDelayMs": {
|
||||
"//": "The delay of the is-there-a-block-around-check around entities in ms. Used in mods which depends on a specific blockstate in the world. Increasing this number can increase TPS if needed. min: 0, max: 3,600,000",
|
||||
"value": 30000
|
||||
},
|
||||
"enablePatronPets": {
|
||||
"//": "Enables pets for Patrons. Will be added in a future release.",
|
||||
"value": true
|
||||
},
|
||||
"downloadNonEnglishTranslations": {
|
||||
"//": "Whether Collective should attempt to download translations if the client language is set to something other than English. Uses files from https://github.com/Serilum/.translations, downloaded via https://translations.serilum.com/. Hosted via CloudFlare Pages.",
|
||||
"value": true
|
||||
},
|
||||
"pushTranslationResourcePack": {
|
||||
"//": "On a server, sends connecting players without Collective a resource pack so custom item names from server-side Serilum mods show up in their own language. Off by default; without it they see the server language instead (see serverLanguage).",
|
||||
"value": false
|
||||
},
|
||||
"requireTranslationResourcePack": {
|
||||
"//": "Marks the resource pack as required, so an accidental decline only kicks the player and asks again next join instead of being remembered for good. Turn off to make it optional.",
|
||||
"value": true
|
||||
},
|
||||
"serverLanguage": {
|
||||
"//": "The language Serilum mod text shows in for players without Collective who aren't sent the resource pack. Uses locale codes like en_us or de_de.",
|
||||
"value": "en_us"
|
||||
},
|
||||
"itemNameTranslationMode": {
|
||||
"//": "How custom item names show for players without Collective when the resource pack is off. 'auto' copies the first player to join and remembers it; 'server' always uses the server language; 'client' uses each player's own language, but those without Collective then see raw keys. options: auto, server, client",
|
||||
"value": "auto"
|
||||
},
|
||||
"configScreenGuiScale": {
|
||||
"//": "The GUI scale of the config screens of Serilum's mods, also set with the button next to the ? on those screens. 0 uses the game's own GUI scale. min: 0, max: 10",
|
||||
"value": 0
|
||||
},
|
||||
"updateMinecraftWindowTitleInDevMode": {
|
||||
"//": "Used in my local development flow. Has no effect in production, only when ran from an IDE with Collective installed.",
|
||||
"value": false
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"//": "These settings are easier to change in-game! Mod List -> Tree Harvester -> Config",
|
||||
"Enable Tree Harvester": {
|
||||
"//": "Whether Tree Harvester is enabled. When disabled, none of its features run and its other settings are hidden. Takes effect after a restart.",
|
||||
"value": true
|
||||
},
|
||||
"mustHoldAxeForTreeHarvest": {
|
||||
"//": "If enabled, tree harvesting only works when a player is holding an axe in the main hand.",
|
||||
"value": true
|
||||
},
|
||||
"axeWhitelist": {
|
||||
"//": "A comma separated list of item ids that can harvest trees too, for modded tools that are not detected as an axe, like paxels and multitools. Use * as a wildcard. An id without a namespace matches items of every mod. Applies after rejoining the world.",
|
||||
"value": "easy_emerald:*_paxel,piercingpaxels:*_paxel,randomisfits:*_multitool,tetra:modular_double"
|
||||
},
|
||||
"treeHarvestWithoutSneak": {
|
||||
"//": "If enabled, tree harvesting works when not holding the sneak button. If disabled it's reversed, and only works when sneaking.",
|
||||
"value": false
|
||||
},
|
||||
"automaticallyFindBottomBlock": {
|
||||
"//": "Whether the mod should attempt to find the actual bottom log of the tree and start there. This means you can break a tree in the middle and it will still completely be felled.",
|
||||
"value": true
|
||||
},
|
||||
"enableFastLeafDecay": {
|
||||
"//": "If enabled, the leaves around a broken tree will quickly disappear.",
|
||||
"value": true
|
||||
},
|
||||
"enableNetherTrees": {
|
||||
"//": "If enabled, the warped stem/crimson trees in the nether will also be chopped down quickly.",
|
||||
"value": true
|
||||
},
|
||||
"enableHugeMushrooms": {
|
||||
"//": "If enabled, giant/huge mushrooms will also be chopped down quickly.",
|
||||
"value": true
|
||||
},
|
||||
"ignorePlayerMadeTrees": {
|
||||
"//": "If enabled, trees with leaves placed by players won't be destroyed.",
|
||||
"value": true
|
||||
},
|
||||
"logBlockBlacklist": {
|
||||
"//": "A comma separated list of block ids that are never harvested as a tree, also when they're detected as a log. Use * as a wildcard. An id without a namespace matches blocks of every mod. For example 'minecraft:jungle_log,*crimson_stem'. Applies after rejoining the world.",
|
||||
"value": ""
|
||||
},
|
||||
"replaceSaplingOnTreeHarvest": {
|
||||
"//": "If enabled, automatically replaces the sapling from the drops when a tree is harvested.",
|
||||
"value": true
|
||||
},
|
||||
"replaceMushroomOnMushroomHarvest": {
|
||||
"//": "If enabled, automatically replaces the sapling from the drops when a huge mushroom is harvested and 'enableHugeMushrooms' is enabled.",
|
||||
"value": true
|
||||
},
|
||||
"loseDurabilityPerHarvestedLog": {
|
||||
"//": "If enabled, for every log harvested, the axe held loses durability.",
|
||||
"value": true
|
||||
},
|
||||
"loseDurabilityModifier": {
|
||||
"//": "Here you can set how much durability chopping down a tree should take from the axe. For example if set to 0.1, this means that every 10 logs take 1 durability. min: 0.001, max: 1",
|
||||
"value": 1.0
|
||||
},
|
||||
"preventAxeBreakingOnTreeHarvest": {
|
||||
"//": "If enabled, harvesting a tree never breaks the axe. The whole tree is still felled, but the axe stops at 1 durability. An axe with 1 durability left can't harvest trees until it's repaired.",
|
||||
"value": true
|
||||
},
|
||||
"increaseExhaustionPerHarvestedLog": {
|
||||
"//": "If enabled, players' exhaustion level increases 0.005 per harvested log (Minecraft's default per broken block) * increaseExhaustionModifier.",
|
||||
"value": true
|
||||
},
|
||||
"increaseExhaustionModifier": {
|
||||
"//": "This determines how much exhaustion should be added to the player per harvested log. By default 0.005 * 1.0. min: 0.001, max: 1",
|
||||
"value": 1.0
|
||||
},
|
||||
"increaseHarvestingTimePerLog": {
|
||||
"//": "If enabled, harvesting time will increase per existing log in the tree. The amount is determined by 'increasedHarvestingTimePerLogModifier'.",
|
||||
"value": true
|
||||
},
|
||||
"increasedHarvestingTimePerLogModifier": {
|
||||
"//": "How much longer it takes to harvest a tree with 'increaseHarvestingTimePerLog' enabled. The actual speed is: newSpeed = originalSpeed / (1 + (logCount * increasedHarvestingTimePerLogModifier)). min: 0.01, max: 10",
|
||||
"value": 0.2
|
||||
},
|
||||
"amountOfLeavesBrokenPerTick": {
|
||||
"//": "How many leaves should be broken per tick after a tree has been harvested. Increasing this will speed up the fast leaf decay, but costs more processing power per tick. min: 1, max: 16",
|
||||
"value": 5
|
||||
},
|
||||
"maxAmountOfLogsBrokenPerHarvest": {
|
||||
"//": "The maximum amount of logs broken in one harvest. The logs closest to the broken log go first, so a bigger tree is only partly felled. Useful against lag from huge modded trees or forests with connected logs. -1 means no limit. min: -1, max: 100,000",
|
||||
"value": -1
|
||||
},
|
||||
"triggerBlockBreakEventPerHarvestedLog": {
|
||||
"//": "If enabled, every harvested log in a tree triggers a block break event, as if broken by hand. This lets other mods see each log, like skill mods that give experience for chopping or claim mods that protect an area. A log is skipped when another mod cancels its event.",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// To disable a certain axe from being able to harvest trees, add an exclamation mark (!) in front of the line,
|
||||
minecraft:wooden_axe,
|
||||
minecraft:copper_axe,
|
||||
minecraft:stone_axe,
|
||||
minecraft:golden_axe,
|
||||
minecraft:iron_axe,
|
||||
minecraft:diamond_axe,
|
||||
minecraft:netherite_axe,
|
||||
betterend:terminite_axe,
|
||||
betterend:thallasium_axe,
|
||||
betterend:aeternium_axe,
|
||||
@@ -1,26 +0,0 @@
|
||||
[general]
|
||||
#The size of overworld biome regions from each mod that uses TerraBlender.
|
||||
overworld_region_size = 3
|
||||
#The weighting of vanilla biome regions in the nether.
|
||||
vanilla_nether_region_weight = 10
|
||||
#The size of nether biome regions from each mod that uses TerraBlender.
|
||||
nether_region_size = 2
|
||||
#The weighting of vanilla biome regions in the overworld.
|
||||
vanilla_overworld_region_weight = 10
|
||||
[end]
|
||||
#The size of edge end biomes.
|
||||
edge_biome_size = 3
|
||||
#The size of highlands end biomes.
|
||||
highlands_biome_size = 4
|
||||
#The weight of Vanilla end barrens biomes.
|
||||
vanilla_end_barrens_weight = 10
|
||||
#The size of island end biomes.
|
||||
island_biome_size = 2
|
||||
#The weight of Vanilla end highlands biomes.
|
||||
vanilla_end_highlands_weight = 10
|
||||
#The weight of Vanilla end midlands biomes.
|
||||
vanilla_end_midlands_weight = 10
|
||||
#The size of midlands end biomes.
|
||||
midlands_biome_size = 4
|
||||
#The weight of Vanilla small end islands biomes.
|
||||
vanilla_small_end_islands_weight = 10
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"houseEdge": 0.9,
|
||||
"minChance": 0.001,
|
||||
"maxChance": 0.9,
|
||||
"spinDurationTicks": 70,
|
||||
"valueOverrides": [],
|
||||
"blacklist": [],
|
||||
"globalValueMultiplier": 1.0,
|
||||
"valueMultipliers": [],
|
||||
"maxTargetCount": 64,
|
||||
"attemptLimit": 0,
|
||||
"attemptWindowSeconds": 0
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
[blueMap]
|
||||
|
||||
# Controls whether Waystones' BlueMap integration should be loaded
|
||||
enabled = true
|
||||
|
||||
# If enabled, sharestones will be tracked as markers on BlueMap.
|
||||
includeSharestones = true
|
||||
|
||||
# If enabled, undiscovered waystones will be tracked as markers on BlueMap.
|
||||
includeUndiscoveredWaystones = false
|
||||
|
||||
# If enabled, waystones will be tracked as markers on BlueMap
|
||||
includeWaystones = true
|
||||
|
||||
[client]
|
||||
|
||||
# If enabled, the text overlay on waystones will no longer always render at full brightness.
|
||||
disableTextGlow = false
|
||||
|
||||
[dynmap]
|
||||
|
||||
# If enabled, Waystones will add markers for waystones and sharestones to Dynmap.
|
||||
enabled = true
|
||||
|
||||
[inventoryButton]
|
||||
|
||||
# The y position of the inventory button in the creative menu.
|
||||
creativeInventoryButtonX = 88
|
||||
|
||||
# The y position of the inventory button in the creative menu.
|
||||
creativeInventoryButtonY = 33
|
||||
|
||||
# Set to 'none' for no inventory button. Set to 'nearest' for an inventory button that teleports to the nearest waystone. Set to 'any' for an inventory button that opens the waystone selection menu. Set to a waystone name for an inventory button that teleports to a specifically named waystone.
|
||||
inventoryButton = ""
|
||||
|
||||
# The x position of the inventory button in the inventory.
|
||||
inventoryButtonX = 58
|
||||
|
||||
# The y position of the inventory button in the inventory.
|
||||
inventoryButtonY = 60
|
||||
|
||||
[journeyMap]
|
||||
|
||||
# If enabled, JourneyMap waypoints will be created for each activated waystone.
|
||||
enabled = true
|
||||
|
||||
# If enabled, JourneyMap waypoints will only be created if the mod 'JourneyMap Integration' is not installed
|
||||
preferJourneyMapIntegrationMod = true
|
||||
|
||||
[rules]
|
||||
|
||||
# Set to true to allow everyone to manage global waystones. Always true if `defaultVisibility` is `global`. Can be overridden by Shogi Rule `may_manage_global_waystones`.
|
||||
allowEveryoneToManageGlobalWaystones = false
|
||||
|
||||
# Set to "team" or "global" to have newly placed or found waystones use that visibility by default.
|
||||
defaultVisibility = "activation"
|
||||
|
||||
# Set to false to simply disable all cooldowns. See rules for more fine-grained control.
|
||||
enableCooldowns = true
|
||||
|
||||
# Set to false to simply disable all durability costs. See rules for more fine-grained control.
|
||||
enableDurability = true
|
||||
|
||||
# Set to true to enable warp modifier items for applying status effects on teleports.
|
||||
enableModifiers = true
|
||||
|
||||
# Set to false to simply disable all xp costs. See rules for more fine-grained control.
|
||||
enableXpCosts = true
|
||||
|
||||
# List of entities that cannot be teleported, either as pet, leashed, or on warp plates.
|
||||
entityDenyList = [ "minecraft:wither" ]
|
||||
|
||||
# Set to "enabled" to have leashed mobs teleport with you. Set to "same_dimension" to have leashed mobs teleport with you only if you're not changing dimensions. Set to "disabled" to disable.
|
||||
transportLeashed = "enabled"
|
||||
|
||||
# Set to "enabled" to have nearby pets teleport with you. Set to "same_dimension" to have nearby pets teleport with you only if you're not changing dimensions. Set to "disabled" to disable.
|
||||
transportPets = "disabled"
|
||||
|
||||
# List of warp requirements in Shogi format.
|
||||
warpRequirements = [
|
||||
"$uses_xp #= and(any(source(is_waystone), source(is_warp_stone)), not(target(is_fleeting_memorial)))",
|
||||
"$uses_xp -> $xp_points_cost = $distance * $xp_per_block",
|
||||
"$uses_xp + is_interdimensional -> $xp_points_cost = $interdimensional_xp_cost",
|
||||
"$uses_xp -> $xp_points_cost = clamp($xp_points_cost, 0, $max_xp_cost)",
|
||||
"is_warp_stone -> damage_item(80)",
|
||||
"is_inventory_button -> cooldown_cost('inventory_button', $inventory_button_cooldown)"
|
||||
]
|
||||
|
||||
# List of variables used in the warpRequirements configuration.
|
||||
warpSettings = [
|
||||
"$xp_per_block = 0.01",
|
||||
"$min_xp_cost = 0",
|
||||
"$max_xp_cost = 27",
|
||||
"$interdimensional_xp_cost = 27",
|
||||
"$inventory_button_cooldown = '300s'"
|
||||
]
|
||||
|
||||
[worldGen]
|
||||
|
||||
# Approximate chunk distance between wild waystones being generated. Set to 0 to disable generation.
|
||||
chunksBetweenWildWaystones = 25
|
||||
|
||||
# Set to 'preset_first' to first use names from the nameGenerationPresets. Set to 'preset_only' to use only those custom names. Set to 'mixed' to have some waystones use custom names, and others random names.
|
||||
nameGenerationMode = "preset_first"
|
||||
|
||||
# These names will be used for the preset name generation mode. See the nameGenerationMode option for more info.
|
||||
nameGenerationPresets = [ ]
|
||||
|
||||
# The template to use when generating new names. Supported placeholders are {Biome} (english biome name) and {MrPork} (the default name generator).
|
||||
nameGenerationTemplate = "{MrPork}"
|
||||
|
||||
# Set to "regular" to have waystones spawn in some villages. Set to "frequent" to have waystones spawn in most villages. Set to "disabled" to disable waystone generation in villages. Waystones will only spawn in vanilla or supported villages.
|
||||
spawnInVillages = "regular"
|
||||
|
||||
# Set to 'default' to only generate the normally textured waystones. Set to 'mossy' or 'sandy' to generate all as that variant. Set to 'biome' to make the style depend on the biome it is generated in.
|
||||
wildWaystoneStyle = "biome"
|
||||
|
||||
# List of dimensions that wild waystones are allowed to spawn in. If left empty, all dimensions except those in wildWaystonesDimensionDenyList will be able to spawn waystones, provided the biomes are in the `has_structure/waystone` tag.
|
||||
wildWaystonesDimensionAllowList = [ "minecraft:the_end", "minecraft:overworld", "minecraft:the_nether" ]
|
||||
|
||||
# List of dimensions that wild waystones are not allowed to spawn in, even if the biome is in the `has_structure/waystone` tag. Only used if wildWaystonesDimensionAllowList is empty.
|
||||
wildWaystonesDimensionDenyList = [ ]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"create_version": "26.301.2",
|
||||
"log": {
|
||||
"verbose": false
|
||||
},
|
||||
"modify_version": "26.301.2"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* PM2 — SurviePote serveur Minecraft (MC 26.3 / Fabric 0.20.0 / Java 25).
|
||||
*
|
||||
* Démarre le serveur DANS le shell Nix du repo (shell.nix => jdk25), sans
|
||||
* dépendre du Java du système.
|
||||
*
|
||||
* cd /srv/mc/server
|
||||
* pm2 start ecosystem.config.cjs
|
||||
* pm2 logs surviepote-mc
|
||||
* pm2 restart surviepote-mc
|
||||
* pm2 stop surviepote-mc
|
||||
* pm2 save
|
||||
*
|
||||
* Les chemins sont résolus depuis ce fichier (__dirname) :
|
||||
* SERVER_DIR = /srv/mc/server
|
||||
* ROOT_DIR = /srv/mc
|
||||
* donc le fichier marche quel que soit l'endroit d'où pm2 est lancé.
|
||||
*/
|
||||
const path = require('path')
|
||||
|
||||
const SERVER_DIR = __dirname
|
||||
const ROOT_DIR = path.resolve(__dirname, '..')
|
||||
const START_SCRIPT = path.join(SERVER_DIR, 'start.sh')
|
||||
|
||||
// cd /srv/mc/server && nix-shell /srv/mc --run "bash /srv/mc/server/start.sh"
|
||||
const command = [
|
||||
`cd ${JSON.stringify(SERVER_DIR)}`,
|
||||
`exec nix-shell ${JSON.stringify(ROOT_DIR)} --run ${JSON.stringify(`bash ${START_SCRIPT}`)}`
|
||||
].join(' && ')
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'surviepote-mc',
|
||||
cwd: SERVER_DIR,
|
||||
|
||||
// On exécute `bash -lc "<commande>"` directement (pas un script Node).
|
||||
script: 'bash',
|
||||
args: ['-lc', command],
|
||||
interpreter: 'none',
|
||||
|
||||
autorestart: true,
|
||||
max_restarts: 10,
|
||||
restart_delay: 5000,
|
||||
min_uptime: '60s',
|
||||
kill_timeout: 30000,
|
||||
treekill: true, // SIGINT/SIGKILL propagés jusqu'à java (arrêt propre)
|
||||
watch: false,
|
||||
|
||||
env: {
|
||||
// On est déjà dans nix-shell : empêche start.sh de s'y relancer.
|
||||
SURVIEPOTE_NIX: '1'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user