Created
November 5, 2025 22:43
-
-
Save nopbxlr/5cc23fb657e3df759b479ad6228f72e4 to your computer and use it in GitHub Desktop.
A simple, lazy EasyAuth (Fabric) to AuthMe (Bukkit) SQLite DB converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| A low effort script to migrate EasyAuth (Fabric) data to AuthMe (Bukkit) format. | |
| Useful if you want to switch server types without forcing all your users to re-register, and maintain their last known positions. | |
| Requires nbtlib: pip install nbtlib | |
| Feed it the server path as the first argument. It'll look for EasyAuth data there and create authme.db in the script's directory. | |
| Check the data! This is a fragile script lacking a lot of checks. I am not responsible for any data loss. Use a staging env. | |
| This only works if EasyAuth data was using bcrypt hashes. It may be possible with a bit more effort to support Argon2 hashes too. | |
| Don't forget to set up AuthMe with bcrypt support. | |
| """ | |
| import os | |
| import sys | |
| import sqlite3 | |
| import json | |
| from datetime import datetime | |
| import glob | |
| import nbtlib | |
| def get_player_coords(server_path): | |
| playerdata_dirs = [] | |
| for root, dirs, files in os.walk(server_path): | |
| if 'playerdata' in dirs: | |
| pd = os.path.join(root, 'playerdata') | |
| if os.path.isdir(pd): | |
| playerdata_dirs.append(pd) | |
| results = {} | |
| for pd in playerdata_dirs: | |
| world_name = os.path.basename(os.path.dirname(pd)) | |
| for nbt_file in glob.glob(os.path.join(pd, '*.dat')): | |
| uuid = os.path.splitext(os.path.basename(nbt_file))[0] | |
| try: | |
| nbt = nbtlib.load(nbt_file) | |
| if 'Pos' in nbt: | |
| pos = nbt['Pos'] | |
| elif 'Data' in nbt: | |
| pos = nbt['Data'].get('Pos', [None, None, None]) | |
| else: | |
| pos = [None, None, None] | |
| x, y, z = pos if len(pos) == 3 else (None, None, None) | |
| results[uuid] = { | |
| 'world': world_name, | |
| 'x': float(x) if x is not None else None, | |
| 'y': float(y) if y is not None else None, | |
| 'z': float(z) if z is not None else None | |
| } | |
| except Exception as e: | |
| print(f'Error reading {nbt_file}: {e}') | |
| return results | |
| # --- Main migration logic --- | |
| def migrate_easyauth_to_authme(server_path, script_path): | |
| # Paths | |
| easyauth_db_path = os.path.join(server_path, 'EasyAuth', 'easyauth.db') | |
| script_dir = os.path.dirname(os.path.abspath(__file__)) | |
| authme_db_path = os.path.join(script_dir, 'authme.db') | |
| # Check if authme.db exists and matches expected structure | |
| create_table_sql = ''' | |
| CREATE TABLE authme ( | |
| id INTEGER, | |
| username VARCHAR(255) NOT NULL UNIQUE, | |
| realname VARCHAR(255) NOT NULL DEFAULT 'Player', | |
| password VARCHAR(255) NOT NULL DEFAULT '', | |
| ip VARCHAR(40), | |
| lastlogin TIMESTAMP, | |
| regip VARCHAR(40), | |
| regdate TIMESTAMP NOT NULL DEFAULT '0', | |
| x DOUBLE NOT NULL DEFAULT '0.0', | |
| y DOUBLE NOT NULL DEFAULT '0.0', | |
| z DOUBLE NOT NULL DEFAULT '0.0', | |
| world VARCHAR(255) NOT NULL DEFAULT 'world', | |
| yaw FLOAT, | |
| pitch FLOAT, | |
| email VARCHAR(255), | |
| isLogged INT NOT NULL DEFAULT '0', | |
| hasSession INT NOT NULL DEFAULT '0', | |
| totp VARCHAR(32), | |
| CONSTRAINT table_const_prim PRIMARY KEY (id) | |
| ); | |
| ''' | |
| if os.path.exists(authme_db_path): | |
| print(f"Warning: {authme_db_path} already exists.") | |
| response = input("Do you want to overwrite it? (y/N): ").strip().lower() | |
| if response == 'y': | |
| os.remove(authme_db_path) | |
| print(f"Deleted old {authme_db_path}.") | |
| conn = sqlite3.connect(authme_db_path) | |
| cur = conn.cursor() | |
| cur.execute(create_table_sql) | |
| conn.commit() | |
| conn.close() | |
| else: | |
| print("Aborted migration. No changes made.") | |
| return | |
| else: | |
| conn = sqlite3.connect(authme_db_path) | |
| cur = conn.cursor() | |
| cur.execute(create_table_sql) | |
| conn.commit() | |
| conn.close() | |
| # Get player coordinates | |
| coords = get_player_coords(server_path) | |
| # Connect to easyauth.db | |
| ea_conn = sqlite3.connect(easyauth_db_path) | |
| ea_cur = ea_conn.cursor() | |
| ea_cur.execute('SELECT username, username_lower, uuid, data FROM easyauth') | |
| easyauth_rows = ea_cur.fetchall() | |
| # Connect to authme.db | |
| am_conn = sqlite3.connect(authme_db_path) | |
| am_cur = am_conn.cursor() | |
| now = int(datetime.now().timestamp()) | |
| next_id = 1 | |
| for username, username_lower, uuid, data_json in easyauth_rows: | |
| try: | |
| data = json.loads(data_json) | |
| except Exception as e: | |
| print(f"Error parsing JSON for {username_lower}: {e}") | |
| continue | |
| password = data.get('password', '') | |
| if not password: | |
| continue | |
| last_ip = data.get('last_ip', '') | |
| # Get coords for this uuid | |
| coord = coords.get(uuid, {}) | |
| x = coord.get('x', 0.0) | |
| y = coord.get('y', 0.0) | |
| z = coord.get('z', 0.0) | |
| world = coord.get('world', 'world') | |
| # Manually assign id | |
| am_cur.execute(''' | |
| INSERT INTO authme (id, username, realname, password, ip, regip, regdate, x, y, z, world, yaw, pitch) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | |
| ''', ( | |
| next_id, | |
| username_lower, | |
| username, | |
| password, | |
| last_ip, | |
| last_ip, | |
| now, | |
| x, | |
| y, | |
| z, | |
| world, | |
| 0.0, | |
| 0.0 | |
| )) | |
| next_id += 1 | |
| am_conn.commit() | |
| ea_conn.close() | |
| am_conn.close() | |
| print(f"Migration complete. Output DB: {authme_db_path}") | |
| if __name__ == '__main__': | |
| if len(sys.argv) < 2: | |
| print('Usage: python migrate_auth.py <server_path>') | |
| sys.exit(1) | |
| migrate_easyauth_to_authme(sys.argv[1], None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment