55
66package meteordevelopment .meteorclient .gui .tabs .builtin ;
77
8+ import meteordevelopment .meteorclient .MeteorClient ;
89import meteordevelopment .meteorclient .gui .GuiTheme ;
910import meteordevelopment .meteorclient .gui .WindowScreen ;
1011import meteordevelopment .meteorclient .gui .renderer .GuiRenderer ;
1112import meteordevelopment .meteorclient .gui .tabs .Tab ;
1213import meteordevelopment .meteorclient .gui .tabs .TabScreen ;
1314import meteordevelopment .meteorclient .gui .tabs .WindowTabScreen ;
1415import meteordevelopment .meteorclient .gui .widgets .containers .WContainer ;
16+ import meteordevelopment .meteorclient .gui .widgets .containers .WHorizontalList ;
1517import meteordevelopment .meteorclient .gui .widgets .containers .WTable ;
1618import meteordevelopment .meteorclient .gui .widgets .pressable .WButton ;
19+ import meteordevelopment .meteorclient .gui .widgets .pressable .WCheckbox ;
1720import meteordevelopment .meteorclient .gui .widgets .pressable .WConfirmedButton ;
1821import meteordevelopment .meteorclient .gui .widgets .pressable .WConfirmedMinus ;
22+ import meteordevelopment .meteorclient .settings .Setting ;
1923import meteordevelopment .meteorclient .systems .profiles .Profile ;
2024import meteordevelopment .meteorclient .systems .profiles .Profiles ;
2125import meteordevelopment .meteorclient .utils .Utils ;
2226import meteordevelopment .meteorclient .utils .misc .NbtUtils ;
27+ import meteordevelopment .meteorclient .utils .render .prompts .OkPrompt ;
2328import net .minecraft .client .gui .screen .Screen ;
24-
29+ import net .minecraft .nbt .NbtCompound ;
30+ import net .minecraft .nbt .NbtElement ;
31+ import net .minecraft .nbt .NbtIo ;
32+ import org .lwjgl .BufferUtils ;
33+ import org .lwjgl .PointerBuffer ;
34+ import org .lwjgl .system .MemoryUtil ;
35+ import org .lwjgl .util .tinyfd .TinyFileDialogs ;
36+
37+ import java .io .DataOutputStream ;
38+ import java .io .File ;
39+ import java .io .FileOutputStream ;
40+ import java .io .IOException ;
41+ import java .nio .ByteBuffer ;
42+ import java .nio .file .Path ;
2543import java .util .ArrayList ;
2644import java .util .List ;
45+ import java .util .Map ;
2746
2847import static meteordevelopment .meteorclient .MeteorClient .mc ;
2948
3049public class ProfilesTab extends Tab {
50+ private static final PointerBuffer filters ;
51+
52+ static {
53+ filters = BufferUtils .createPointerBuffer (1 );
54+
55+ ByteBuffer pngFilter = MemoryUtil .memASCII ("*.nbt" );
56+
57+ filters .put (pngFilter );
58+ filters .rewind ();
59+ }
60+
3161 public ProfilesTab () {
3262 super ("Profiles" );
3363 }
@@ -54,9 +84,31 @@ public void initWidgets() {
5484
5585 add (theme .horizontalSeparator ()).expandX ();
5686
87+ WHorizontalList l = add (theme .horizontalList ()).expandX ().widget ();
88+
5789 // Create
58- WButton create = add (theme .button ("Create" )).expandX ().widget ();
90+ WButton create = l .add (theme .button ("Create" )).expandX ().widget ();
91+ create .tooltip = "Create new profile" ;
5992 create .action = () -> mc .setScreen (new EditProfileScreen (theme , null , this ::reload ));
93+
94+ // Import
95+ WButton importBtn = l .add (theme .button ("Import" )).expandX ().widget ();
96+ importBtn .tooltip = "Import profile" ;
97+ importBtn .action = () -> {
98+ try {
99+ Profile imported = importProfile ();
100+ if (imported != null ) MeteorClient .LOG .info ("Successfully imported profile '{}'." , imported .name .get ());
101+ reload ();
102+ } catch (IOException e ) {
103+ MeteorClient .LOG .error ("Error importing profile" , e );
104+ OkPrompt .create ()
105+ .title ("Failure importing profile" )
106+ .message ("There was an error importing the profile." )
107+ .message ("Error: %d" , e .getMessage ())
108+ .dontShowAgainCheckboxVisible (false )
109+ .show ();
110+ }
111+ };
60112 }
61113
62114 private void initTable (WTable table ) {
@@ -73,6 +125,9 @@ private void initTable(WTable table) {
73125 WButton load = table .add (theme .button ("Load" )).widget ();
74126 load .action = profile ::load ;
75127
128+ WButton export = table .add (theme .button ("Export" )).widget ();
129+ export .action = () -> mc .setScreen (new ExportProfileScreen (theme , profile ));
130+
76131 WButton edit = table .add (theme .button (GuiRenderer .EDIT )).widget ();
77132 edit .action = () -> mc .setScreen (new EditProfileScreen (theme , profile , this ::reload ));
78133
@@ -86,6 +141,41 @@ private void initTable(WTable table) {
86141 }
87142 }
88143
144+ private Profile importProfile () throws IOException {
145+ String file = TinyFileDialogs .tinyfd_openFileDialog ("Select profile to import" , null , filters , null , false );
146+ if (file == null ) return null ;
147+ File profileFile = new File (file );
148+
149+ NbtCompound nbt = NbtIo .read (profileFile .toPath ());
150+
151+ Profile p = new Profile ();
152+ p .name .set (nbt .getString ("name" , profileFile .getName ()));
153+ //noinspection ResultOfMethodCallIgnored
154+ p .getFile ().mkdirs ();
155+
156+ nbt .remove ("name" );
157+ for (Map .Entry <String , NbtElement > entry : nbt .entrySet ()) {
158+ String filename = entry .getKey ();
159+
160+ switch (filename ) {
161+ case "hud.nbt" -> p .hud .set (true );
162+ case "macros.nbt" -> p .macros .set (true );
163+ case "modules.nbt" -> p .modules .set (true );
164+ default -> {
165+ if (filename .endsWith (".nbt" )) p .waypoints .set (true );
166+ }
167+ }
168+
169+ File f = new File (p .getFile (), filename );
170+ NbtIo .write (entry .getValue (), new DataOutputStream (new FileOutputStream (f )));
171+ }
172+
173+ Profiles .get ().getAll ().add (p );
174+ Profiles .get ().save ();
175+
176+ return p ;
177+ }
178+
89179 @ Override
90180 public boolean toClipboard () {
91181 return NbtUtils .toClipboard (Profiles .get ());
@@ -154,4 +244,77 @@ protected void onClosed() {
154244 if (action != null ) action .run ();
155245 }
156246 }
247+
248+ private static class ExportProfileScreen extends WindowScreen {
249+ private final Profile profile ;
250+
251+ public ExportProfileScreen (GuiTheme theme , Profile profile ) {
252+ super (theme , "Export Profile" );
253+ this .profile = profile ;
254+ }
255+
256+ @ Override
257+ public void initWidgets () {
258+ add (theme .label ("Select which profile settings to export." ));
259+
260+ WContainer settingsContainer = add (theme .verticalList ()).expandX ().minWidth (400 ).widget ();
261+
262+ settingsContainer .add (theme .horizontalSeparator ()).expandX ().widget ();
263+
264+ WCheckbox hud = addBool (settingsContainer , profile .settings .get ("hud" , Boolean .class ));
265+ WCheckbox macros = addBool (settingsContainer , profile .settings .get ("macros" , Boolean .class ));
266+ WCheckbox modules = addBool (settingsContainer , profile .settings .get ("modules" , Boolean .class ));
267+ WCheckbox waypoints = addBool (settingsContainer , profile .settings .get ("waypoints" , Boolean .class ));
268+
269+ add (theme .horizontalSeparator ()).expandX ().widget ();
270+
271+ WButton export = add (theme .button ("Export profile" )).expandX ().widget ();
272+ export .action = () -> {
273+ exportProfile (profile , hud .checked , macros .checked , modules .checked , waypoints .checked );
274+ close ();
275+ };
276+ }
277+
278+ private WCheckbox addBool (WContainer container , Setting <Boolean > setting ) {
279+ WHorizontalList boolList = container .add (theme .horizontalList ()).expandX ().widget ();
280+ boolList .add (theme .label (setting .title )).widget ().tooltip = setting .description ;
281+
282+ WCheckbox c = theme .checkbox (setting .get ());
283+ boolList .add (c ).expandCellX ().right ();
284+
285+ return c ;
286+ }
287+
288+ private void exportProfile (Profile profile , boolean hud , boolean macros , boolean modules , boolean waypoints ) {
289+ String path = TinyFileDialogs .tinyfd_saveFileDialog ("Save profile" , profile .name .get (), filters , null );
290+ if (path == null ) return ;
291+ Path p = Path .of (path .endsWith (".nbt" ) ? path : path + ".nbt" );
292+
293+ NbtCompound nbt = new NbtCompound ();
294+ nbt .putString ("name" , profile .name .get ());
295+
296+ try {
297+ for (File f : profile .getFile ().listFiles ()) {
298+ if (f .getName ().equals ("hud.nbt" ) && hud ||
299+ f .getName ().equals ("macros.nbt" ) && macros ||
300+ f .getName ().equals ("modules.nbt" ) && modules
301+ ) {
302+ nbt .put (f .getName (), NbtIo .read (f .toPath ()));
303+ }
304+ else if (f .getName ().endsWith (".nbt" ) && waypoints )
305+ nbt .put (f .getName (), NbtIo .read (f .toPath ()));
306+ }
307+
308+ NbtIo .write (nbt , p );
309+ } catch (IOException e ) {
310+ MeteorClient .LOG .error ("Error serialising profile {} to a file" , profile .name .get (), e );
311+ OkPrompt .create ()
312+ .title ("Failure exporting profile" )
313+ .message ("There was an error serialising or exporting the profile %d." , profile .name .get ())
314+ .message ("Error: %d" , e .getMessage ())
315+ .dontShowAgainCheckboxVisible (false )
316+ .show ();
317+ }
318+ }
319+ }
157320}
0 commit comments