ConanServerManager/lib/pages/ModManager.dart
zontreck e2d64a2a9d Add a comment field to mods
ADDTL; Edited the deserialization routine for mods to make it less prone to errors in the event a mod does not contain every field in the NBT data, ex. new comment field would have crashed the deserializer, causing it to skip past the mod or fully reset server settings.
2025-01-04 17:25:51 -07:00

322 lines
9.7 KiB
Dart

import 'package:flutter/material.dart';
import '../structs/mod.dart';
import '../structs/settings.dart';
class ModManager extends StatefulWidget {
Settings settings;
ModManager({super.key, required this.settings});
@override
ModManagerState createState() => ModManagerState(settings: settings);
}
class ModManagerState extends State<ModManager> {
Settings settings;
ModManagerState({required this.settings});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Conan Exiles Server Manager - Mod Manager"),
backgroundColor: Color.fromARGB(255, 100, 0, 0),
actions: [
IconButton(
onPressed: () async {
for (Mod M in settings.inst!.mods) {
M.enabled = false;
await Future.delayed(Duration(milliseconds: 500));
settings.loggedInUser!
.sendDiscordActionLog("${M.mod_name} was disabled");
setState(() {});
}
},
icon: Icon(Icons.swipe_down)),
IconButton(
onPressed: () async {
for (Mod M in settings.inst!.mods) {
M.enabled = true;
await Future.delayed(Duration(milliseconds: 500));
settings.loggedInUser!
.sendDiscordActionLog("${M.mod_name} was enabled");
setState(() {});
}
},
icon: Icon(Icons.swipe_up))
],
),
body: ReorderableListView.builder(
onReorder: (oldIndex, newIndex) {
if (oldIndex < newIndex) {
// From top to Bottom
int end = newIndex - 1;
Mod item = settings.inst!.mods[oldIndex];
int i = 0;
int local = oldIndex;
do {
settings.inst!.mods[local] = settings.inst!.mods[++local];
i++;
} while (i < end - oldIndex);
settings.inst!.mods[end] = item;
settings.loggedInUser!.sendDiscordActionLog(
"Reordered Mod List\n\n${item.mod_name} is now in load order $end");
} else if (oldIndex > newIndex) {
//From bottom to top
Mod item = settings.inst!.mods[oldIndex];
for (int i = oldIndex; i > newIndex; i--) {
settings.inst!.mods[i] = settings.inst!.mods[i - 1];
}
settings.inst!.mods[newIndex] = item;
settings.loggedInUser!.sendDiscordActionLog(
"Reordered Mod List\n\n${item.mod_name} is now in load order $newIndex");
}
setState(() {
settings.Write();
});
},
itemBuilder: (ctx, idx) {
Mod mod = settings.inst!.mods[idx];
return Padding(
key: Key(mod.mod_instance_id()),
padding: EdgeInsets.all(12),
child: ListTile(
title: Text(mod.mod_name),
subtitle: Text(
"ID: ${mod.mod_id}\nLoad Order: $idx\nEnabled: ${mod.enabled}"),
onTap: () async {
final reply = await Navigator.pushNamed(
context, "/server/mods/edit",
arguments: Mod(
mod_id: mod.mod_id,
mod_name: mod.mod_name,
mod_pak: mod.mod_pak,
mod_hash: mod.mod_hash,
newMod: false,
enabled: mod.enabled));
if (reply != null) {
ModEditReturnArgs MERA = reply as ModEditReturnArgs;
if (MERA.delete) {
setState(() {
settings.loggedInUser!.sendDiscordActionLog(
"Deleted Mod: ${settings.inst!.mods[idx].mod_name}");
settings.inst!.mods.removeAt(idx);
});
return;
}
setState(() {
settings.inst!.mods[idx] = MERA.mod!;
settings.loggedInUser!.sendDiscordActionLog(
"Edited Mod: ${MERA.mod!.mod_name}");
});
}
},
),
);
},
itemCount: settings.inst!.mods.length,
),
floatingActionButton: ElevatedButton(
child: Icon(Icons.add),
onPressed: () async {
// Open new mod info screen
final reply = await Navigator.pushNamed(context, "/server/mods/edit",
arguments: Mod(newMod: true));
if (reply != null) {
ModEditReturnArgs MERA = reply as ModEditReturnArgs;
setState(() {
settings.inst!.mods.add(MERA.mod!);
});
settings.loggedInUser!
.sendDiscordActionLog("Added Mod: ${MERA.mod!.mod_name}");
}
},
),
);
}
}
class ModPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => ModPageState();
}
class ModPageState extends State<ModPage> {
TextEditingController id = TextEditingController();
TextEditingController name = TextEditingController();
String instance = "";
bool isNewMod = false;
String pak = "Not initialized";
String hash = "";
bool enabled = false;
TextEditingController comment = TextEditingController();
@override
void didChangeDependencies() {
final args = ModalRoute.of(context)!.settings.arguments as Mod?;
if (args != null) {
id.text = args.mod_id.toString();
name.text = args.mod_name;
isNewMod = args.newMod;
instance = args.mod_instance_id();
pak = args.mod_pak;
hash = args.mod_hash;
enabled = args.enabled;
comment.text = args.comment;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Mod Editor"),
backgroundColor: Color.fromARGB(255, 100, 0, 0),
),
floatingActionButton: ElevatedButton(
child: Text("Save"),
onPressed: () {
int idVal = 0;
try {
idVal = int.parse(id.text);
} catch (E) {}
Navigator.pop(
context,
ModEditReturnArgs(
delete: false,
mod: Mod(
mod_id: idVal,
mod_name: name.text,
newMod: false,
enabled: enabled,
comment: comment.text)));
},
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(children: [
Row(
children: [
SizedBox(
width: 150,
child: ListTile(
leading: Icon(Icons.abc_rounded),
title: Text("Mod Name"),
)),
Expanded(
child: TextField(
controller: name,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4)))),
)
],
),
SizedBox(
height: 16,
),
Row(
children: [
SizedBox(
width: 150,
child: ListTile(
leading: Icon(Icons.perm_identity), title: Text("Mod ID")),
),
Expanded(
child: TextField(
controller: id,
keyboardType: TextInputType.number,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4))),
))
],
),
SizedBox(
height: 16,
),
ListTile(
title: Text("Mod Instance ID"),
subtitle: Text(instance),
),
ListTile(
title: Text("Mod Pak File: $pak"),
subtitle: Text("Mod pak file name as detected during downloading"),
),
ListTile(
title: Text("Mod Hash"),
subtitle: Text(hash),
),
SwitchListTile(
value: enabled,
onChanged: (V) {
setState(() {
enabled = V;
});
Settings.Instance.loggedInUser!.sendDiscordActionLog(
"${name.text} was ${enabled ? "enabled" : "disabled"}");
},
title: Text("Enabled"),
subtitle: Text("Whether mod is enabled or not"),
),
Row(
children: [
SizedBox(
width: 150,
child: ListTile(
leading: Icon(Icons.comment),
title: Text("Comment"),
),
),
Expanded(
child: TextField(
controller: comment,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8))),
))
],
),
if (!isNewMod)
ElevatedButton(
onPressed: () {
Navigator.pop(context, ModEditReturnArgs(delete: true));
},
child: Row(
children: [
Icon(Icons.delete),
SizedBox(
width: 4,
),
Text("Remove Mod")
],
)),
]),
),
);
}
}
class ModEditReturnArgs {
bool delete;
Mod? mod;
ModEditReturnArgs({required this.delete, this.mod});
}