Python code for MPUVR Level Select Tool v1.0.2

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import subprocess
import os
import sys
import time
import psutil
import logging
from datetime import datetime
import ctypes
import webbrowser
import json

def get_base_path():
    """ Get the base path for the application """
    if getattr(sys, 'frozen', False):
        # If the application is run as a bundle, the PyInstaller bootloader
        # extends the sys module by a flag frozen=True and sets the app 
        # path into variable _MEIPASS'.
        return os.path.dirname(sys.executable)
    else:
        return os.path.dirname(os.path.abspath(__file__))

def get_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    base_path = get_base_path()
    return os.path.join(base_path, relative_path)

class LevelChangeApp:
    def __init__(self, master):
        self.master = master
        master.title("Marvel Powers United VR Level Select Tool v1.0.2")
        master.geometry("800x850")

        # Set up dark theme
        self.style = ttk.Style()
        self.style.theme_use('clam')
        self.style.configure('.', background='#2E2E2E', foreground='white')
        self.style.configure('TButton', background='#3E3E3E', foreground='white')
        self.style.map('TButton', background=[('active', '#4E4E4E')])
        master.configure(bg='#2E2E2E')

        # Configure styles for better readability
        self.style.configure('TEntry', fieldbackground='#4E4E4E', foreground='white')
        self.style.configure('TCombobox', fieldbackground='#4E4E4E', foreground='black', selectbackground='#6E6E6E', selectforeground='white')
        self.style.map('TCombobox', fieldbackground=[('readonly', '#4E4E4E')], selectbackground=[('readonly', '#6E6E6E')])

        self.setup_logging()

        # Initialize attributes
        self.game_pid = None
        self.uuuclient_process = None
        self.console_key = '`'  # Default to backtick
        self.dll_injected = False

        self.load_settings()
        self.create_widgets()
        self.create_menu()

    def load_settings(self):
        self.settings_file = get_path("settings.json")
        if os.path.exists(self.settings_file):
            with open(self.settings_file, "r") as f:
                self.settings = json.load(f)
        else:
            self.settings = {
                "console_key": "`",
                "dont_show_nick": False,
                "dont_show_danger": False
            }

    def save_settings(self):
        with open(self.settings_file, "w") as f:
            json.dump(self.settings, f)

    def create_menu(self):
        menubar = tk.Menu(self.master)
        self.master.config(menu=menubar)

        main_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Menu", menu=main_menu)

        main_menu.add_command(label="About", command=self.show_about)
        main_menu.add_command(label="Contact", command=self.show_contact)
        main_menu.add_command(label="Options", command=self.show_options)
        main_menu.add_separator()
        main_menu.add_command(label="Exit", command=self.exit_application)

    def show_options(self):
        options_window = tk.Toplevel(self.master)
        options_window.title("Options")
        options_window.geometry("300x200")
        options_window.configure(bg='#2E2E2E')

        ttk.Button(options_window, text="Reset All Settings", command=self.reset_settings).pack(pady=10)

    def reset_settings(self):
        self.settings = {
            "console_key": "`",
            "dont_show_nick": False,
            "dont_show_danger": False
        }
        self.save_settings()
        self.key_var.set(self.settings["console_key"])
        messagebox.showinfo("Settings Reset", "All settings have been reset to default.")

    def update_console_key(self):
        new_key = self.key_var.get()
        if len(new_key) != 1:
            messagebox.showerror("Invalid Key", "Please enter a single character.")
            return
        self.settings["console_key"] = new_key
        self.save_settings()
        self.log_to_console(f"Console key updated to: {new_key}")
        messagebox.showwarning("UUUClient Settings", "Remember to update the console key in UUUClient as well!")

    def load_level(self):
        if not self.game_pid:
            messagebox.showerror("Error", "Game is not running. Please start the game first.")
            return

        if not self.dll_injected:
            messagebox.showerror("Error", "DLL is not injected. Please inject the DLL first.")
            return

        level = self.level_var.get()
        if level == "Choose a level":
            self.log_to_console("Warning: Please select a level first")
            return

        if level in ["Nick Test Arena (Wolverine)", "Danger Room - Room for Training (Wolverine)"]:
            if not self.show_warning_popup(level):
                return

        command = self.get_command_for_level(level)
        self.send_command(command)

    def show_warning_popup(self, level):
        if (level == "Nick Test Arena (Wolverine)" and self.settings["dont_show_nick"]) or           (level == "Danger Room - Room for Training (Wolverine)" and self.settings["dont_show_danger"]):
            return True

        result = messagebox.askokcancel(
            "Warning",
            "NOTE: This level requires you to go to the Hub first and choose Wolverine before loading this level! "
            "Once the level loads you can switch characters.",
            icon='warning'
        )

        if result:
            dont_show_var = tk.BooleanVar()
            dont_show_window = tk.Toplevel(self.master)
            dont_show_window.title("Don't show again")
            dont_show_window.geometry("300x100")
            dont_show_window.configure(bg='#2E2E2E')

            ttk.Checkbutton(dont_show_window, text="Don't show this warning again for this level", 
                            variable=dont_show_var).pack(pady=10)
            ttk.Button(dont_show_window, text="OK", command=dont_show_window.destroy).pack()

            self.master.wait_window(dont_show_window)

            if dont_show_var.get():
                if level == "Nick Test Arena (Wolverine)":
                    self.settings["dont_show_nick"] = True
                else:
                    self.settings["dont_show_danger"] = True
                self.save_settings()

        return result

    def setup_logging(self):
        log_file = get_path("levelselectscript.log")
        logging.basicConfig(filename=log_file, level=logging.INFO,
                            format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
        logging.info("----------------------------------------")
        logging.info("Script started")

    def create_widgets(self):
        # Title
        title_label = tk.Label(self.master, text="Marvel Powers United VR Level Select Tool v1.0.2", font=("Arial", 16, "bold"), bg='#2E2E2E', fg='blue')
        title_label.pack(pady=10)

        # Modify Start Game button to Start/Restart Game
        game_frame = ttk.Frame(self.master)
        game_frame.pack(pady=10, fill=tk.X)
        ttk.Button(game_frame, text="Start/Restart Game", command=self.start_restart_game).pack(side=tk.LEFT, expand=True, padx=(0, 5))
        ttk.Button(game_frame, text="Inject DLL", command=self.inject_dll).pack(side=tk.LEFT, expand=True, padx=(5, 0))


        # UUUClient control frame
        uuu_frame = ttk.Frame(self.master)
        uuu_frame.pack(pady=10, fill=tk.X)
        ttk.Button(uuu_frame, text="Launch UUUClient", command=self.launch_uuuclient).pack(side=tk.LEFT, expand=True, padx=(0, 5))
        ttk.Button(uuu_frame, text="Close UUUClient", command=self.close_uuuclient).pack(side=tk.LEFT, expand=True, padx=(5, 0))

        # Console key frame
        key_frame = ttk.Frame(self.master)
        key_frame.pack(pady=10, fill=tk.X)

        # Left side: Label and Entry
        left_frame = ttk.Frame(key_frame)
        left_frame.pack(side=tk.LEFT, expand=True, padx=(0, 5))
        ttk.Label(left_frame, text="Console Key:", foreground='white', background='#2E2E2E').pack(side=tk.LEFT, padx=(0, 5))
        self.key_var = tk.StringVar(value=self.console_key)
        self.key_entry = ttk.Entry(left_frame, textvariable=self.key_var, width=5, style='TEntry')
        self.key_entry.pack(side=tk.LEFT)

        # Right side: Update button
        ttk.Button(key_frame, text="Update Key", command=self.update_console_key).pack(side=tk.LEFT, expand=True, padx=(5, 0))

        # Level selection frame
        level_frame = ttk.Frame(self.master)
        level_frame.pack(pady=10, fill=tk.X)
        self.level_var = tk.StringVar()
        self.level_dropdown = ttk.Combobox(level_frame, textvariable=self.level_var, state="readonly", style='TCombobox', width=50)
        self.level_dropdown['values'] = (
            "Game Menu", "Ops - Hub", "Tutorial on Moving",
            "Stark Tower - Tutorial Intro Sequence", "Jotunheim", "Throne Room - Asgard",
            "Palace - Attilan", "Void - Dark Dimension", "Forest - Halfworld",
            "Marketplace - Knowhere Marketplace", "Downtown - Downtown New York",
            "Arena - Sakaar Arena", "Research Lab - Wakanda", "Hangar - X-Mansion Hangar",
            "Danger Room - Room for Training (Wolverine)", "Sanctuary II - Thanos Boss Battle",
            "Nick Test Arena (Wolverine)"
        )
        self.level_dropdown.set("Choose a level")
        self.level_dropdown.pack(side=tk.LEFT, expand=True, padx=(0, 5))
        ttk.Button(level_frame, text="Load Selected Level", command=self.load_level).pack(side=tk.LEFT, expand=True, padx=(5, 0))

        # Console output
        self.console = scrolledtext.ScrolledText(self.master, width=90, height=20, bg='#1E1E1E', fg='white')
        self.console.pack(pady=10)

        # Add Exit Game button above Exit button
        ttk.Button(self.master, text="Exit Game", command=self.exit_game).pack(pady=5)
        ttk.Button(self.master, text="Exit Tool", command=self.exit_application).pack(pady=5)



    def show_about(self):
        about_window = tk.Toplevel(self.master)
        about_window.title("About")
        about_window.geometry("400x200")
        about_window.configure(bg='#2E2E2E')

        about_text = ("DeliciousMeatPop from ARMGDDN Games made this tool for the\n"
                      "Marvel Powers United VR Revival Discord")

        ttk.Label(about_window, text=about_text, background='#2E2E2E', foreground='white', wraplength=380).pack(pady=10)

        def create_hyperlink(parent, text, url):
            link = ttk.Label(parent, text=text, foreground="light blue", cursor="hand2", background='#2E2E2E')
            link.pack()
            link.bind("<Button-1>", lambda e: webbrowser.open_new(url))
            return link

        create_hyperlink(about_window, "ARMGDDN Games", "https://t.me/ARMGDDNGames")
        create_hyperlink(about_window, "Marvel Powers United VR Revival Discord", "https://discord.com/invite/28fRTaTSd9")

    def show_contact(self):
        contact_window = tk.Toplevel(self.master)
        contact_window.title("Contact")
        contact_window.geometry("300x200")
        contact_window.configure(bg='#2E2E2E')

        ttk.Button(contact_window, text="DMP on Reddit", command=lambda: webbrowser.open("https://www.reddit.com/user/DeliciousMeatPop/")).pack(pady=5)
        ttk.Button(contact_window, text="DMP on Telegram", command=lambda: webbrowser.open("https://t.me/SickSoThr33")).pack(pady=5)
        ttk.Button(contact_window, text="DMP on Discord", command=lambda: webbrowser.open("https://discordapp.com/users/191105213808115712")).pack(pady=5)
        ttk.Button(contact_window, text="DMP on Github", command=lambda: webbrowser.open("https://github.com/KaladinDMP")).pack(pady=5)

    def exit_application(self):
        if messagebox.askokcancel("Exit", "Are you sure you want to exit?"):
            self.master.quit()

    def log_to_console(self, message):
        self.console.insert(tk.END, f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}\n")
        self.console.see(tk.END)
        logging.info(message)

    def exit_game(self):
        if self.game_pid:
            try:
                process = psutil.Process(self.game_pid)
                process.terminate()
                self.log_to_console("Game process terminated")
                self.game_pid = None
                self.dll_injected = False
            except psutil.NoSuchProcess:
                self.log_to_console("Game process not found")
        else:
            self.log_to_console("No game process to terminate")

    def wait_for_process(self, process_name):
        while True:
            for proc in psutil.process_iter(['name', 'pid']):
                if proc.name() == process_name:
                    self.game_pid = proc.pid
                    self.log_to_console(f"{process_name} process found with PID: {self.game_pid}")
                    return
            time.sleep(1)

    def start_restart_game(self):
        if self.game_pid:
            self.exit_game()
            time.sleep(2)  # Wait for the game to close

        self.log_to_console("Starting game")
        game_path = get_path(os.path.join("WindowsNoEditor", "MarvelVR", "Binaries", "Win64", "MarvelVR-Win64-Shipping.exe"))
        subprocess.Popen(game_path)
        self.log_to_console("Game process started. Please wait...")
        self.wait_for_process("MarvelVR-Win64-Shipping.exe")

    def inject_dll(self):
        if not self.game_pid:
            self.log_to_console("Error: Game process not found. Please start the game first.")
            return

        self.log_to_console("Launching minimized UUUClient and injecting DLL")
        uuuclient_path = get_path(os.path.join("InjectUUU", "UUUClient.exe"))

        if not os.path.exists(uuuclient_path):
            self.log_to_console(f"Error: UUUClient.exe not found at {uuuclient_path}")
            return

        try:
            # Constants from the win32 API for minimizing the window
            SW_SHOWMINIMIZED = 2

            # Launch minimized UUUClient
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = SW_SHOWMINIMIZED
            self.uuuclient_process = subprocess.Popen(uuuclient_path, startupinfo=startupinfo)

            # Wait for UUUClient to initialize
            time.sleep(2)

            injector_path = get_path(os.path.join("InjectUUU", "Injector.exe"))
            uuu_dll = get_path(os.path.join("InjectUUU", "UniversalUE4Unlocker.dll"))

            if not os.path.exists(injector_path):
                self.log_to_console(f"Error: Injector.exe not found at {injector_path}")
                return
            if not os.path.exists(uuu_dll):
                self.log_to_console(f"Error: UniversalUE4Unlocker.dll not found at {uuu_dll}")
                return

            # Run the injector
            subprocess.run([injector_path, "--process-id", str(self.game_pid), "-inject", uuu_dll], 
                           check=True, 
                           startupinfo=startupinfo)

            self.log_to_console("UUU DLL injected successfully")
            self.dll_injected = True

            # Wait for 10 seconds
            self.log_to_console("Waiting 10 seconds before closing UUUClient")
            time.sleep(10)

            # Close UUUClient
            if self.uuuclient_process:
                self.uuuclient_process.terminate()
                self.uuuclient_process = None
                self.log_to_console("UUUClient closed")

        except subprocess.CalledProcessError as e:
            self.log_to_console(f"Error injecting DLL: {e}")
            self.dll_injected = False
        except Exception as e:
            self.log_to_console(f"Unexpected error: {e}")
        finally:
            if self.uuuclient_process:
                self.uuuclient_process.terminate()
                self.uuuclient_process = None

    def launch_uuuclient(self):
        # Close UUUClient if it's running
        if self.uuuclient_process:
            self.uuuclient_process.terminate()
            self.uuuclient_process = None
            self.log_to_console("UUUClient closed")

        # Launch visible UUUClient
        self.log_to_console("Launching visible UUUClient")
        uuuclient_path = get_path(os.path.join("InjectUUU", "UUUClient.exe"))
        self.uuuclient_process = subprocess.Popen(uuuclient_path)
        self.log_to_console("Visible UUUClient launched")


    def close_uuuclient(self):
        if self.uuuclient_process:
            self.log_to_console("Closing UUUClient")
            self.uuuclient_process.terminate()
            self.uuuclient_process = None
            self.log_to_console("UUUClient closed")
        else:
            self.log_to_console("No UUUClient is running")

    def get_command_for_level(self, level):
        commands = {
            "Game Menu": "open menu",
            "Ops - Hub": "open Ops",
            "Tutorial on Moving": "open MoveTutorial",
            "Stark Tower - Tutorial Intro Sequence": "open StarkTower",
            "Jotunheim": "open Jotunheim",
            "Throne Room - Asgard": "open ThroneRoom",
            "Palace - Attilan": "open Palace",
            "Void - Dark Dimension": "open Void",
            "Forest - Halfworld": "open Forest",
            "Marketplace - Knowhere Marketplace": "open Marketplace",
            "Downtown - Downtown New York": "open DownTown",
            "Arena - Sakaar Arena": "open Arena",
            "Research Lab - Wakanda": "open ResearchLab",
            "Hangar - X-Mansion Hangar": "open Hangar",
            "Danger Room - Room for Training (Wolverine)": "open DangerRoom03",
            "Sanctuary II - Thanos Boss Battle": "open SanctuaryII",
            "Nick Test Arena (Wolverine)": "open Nick_TestArena"
        }
        return commands.get(level, "")

    def send_command(self, command):
        self.log_to_console(f"Sending command: {command}")
        try:
            subprocess.run(['powershell', '-Command', f"""
                $wshell = New-Object -ComObject wscript.shell;
                $wshell.AppActivate({self.game_pid});
                Start-Sleep -Milliseconds 500;
                $wshell.SendKeys('{self.console_key}');
                Start-Sleep -Milliseconds 500;
                $wshell.SendKeys('{command}');
                Start-Sleep -Milliseconds 500;
                $wshell.SendKeys('{{ENTER}}');
            """])
            self.log_to_console(f"Command sent: {command}")
        except Exception as e:
            self.log_to_console(f"Error: Failed to send command: {str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = LevelChangeApp(root)
    root.mainloop()
Edit Report
Pub: 02 Sep 2024 22:06 UTC
Views: 1190