mirror of
				https://github.com/DarkflameUniverse/NexusDashboard.git
				synced 2025-10-30 20:11:54 +00:00 
			
		
		
		
	syntax/linting fixes
This commit is contained in:
		| @@ -10,7 +10,7 @@ from app.forms import CustomUserManager | ||||
| from flask_user import user_registered, current_user, user_logged_in | ||||
| from flask_wtf.csrf import CSRFProtect | ||||
| from flask_apscheduler import APScheduler | ||||
| from app.luclient import query_cdclient, register_luclient_jinja_helpers | ||||
| from app.luclient import register_luclient_jinja_helpers | ||||
|  | ||||
| from app.commands import init_db, init_accounts, load_property, gen_image_cache, gen_model_cache | ||||
| from app.models import Account, AccountInvitation, AuditLog | ||||
| @@ -40,13 +40,12 @@ def create_app(): | ||||
|             app.logger.info( | ||||
|                 f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered \ | ||||
|                 using Play Key ID {play_key_used.id} : {play_key_used.key_string}" | ||||
|                 ) | ||||
|             ) | ||||
|             db.session.add(play_key_used) | ||||
|             db.session.commit() | ||||
|         else: | ||||
|             app.logger.info(f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered") | ||||
|  | ||||
|  | ||||
|     @user_logged_in.connect_via(app) | ||||
|     def _after_login_hook(sender, user, **extra): | ||||
|         app.logger.info(f"{user.username} Logged in") | ||||
| @@ -55,7 +54,7 @@ def create_app(): | ||||
|     @app.template_filter('ctime') | ||||
|     def timectime(s): | ||||
|         if s: | ||||
|             return time.ctime(s) # or datetime.datetime.fromtimestamp(s) | ||||
|             return time.ctime(s)  # or datetime.datetime.fromtimestamp(s) | ||||
|         else: | ||||
|             return "Never" | ||||
|  | ||||
| @@ -246,6 +245,7 @@ def gm_level(gm_level): | ||||
|         return wrapper | ||||
|     return decorator | ||||
|  | ||||
|  | ||||
| def log_audit(message): | ||||
|     AuditLog( | ||||
|         account_id=current_user.id, | ||||
|   | ||||
| @@ -1,10 +1,8 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, current_app, flash, current_app | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, current_app, flash | ||||
| from flask_user import login_required, current_user | ||||
| import json | ||||
| from datatables import ColumnDT, DataTables | ||||
| import datetime | ||||
| import time | ||||
| from app.models import Account, AccountInvitation, db | ||||
| from app.models import Account, db | ||||
| from app.schemas import AccountSchema | ||||
| from app import gm_level, log_audit | ||||
| from app.forms import EditGMLevelForm | ||||
| @@ -13,6 +11,7 @@ accounts_blueprint = Blueprint('accounts', __name__) | ||||
|  | ||||
| account_schema = AccountSchema() | ||||
|  | ||||
|  | ||||
| @accounts_blueprint.route('/', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -38,7 +37,7 @@ def edit_gm_level(id): | ||||
|     if current_user.id == int(id): | ||||
|         flash("You cannot your own GM Level", "danger") | ||||
|         return redirect(request.referrer if request.referrer else url_for("main.index")) | ||||
|     account_data = Account.query.filter(Account.id==id).first() | ||||
|     account_data = Account.query.filter(Account.id == id).first() | ||||
|     if account_data.gm_level >= 8 and current_user.gm_level == 8: | ||||
|         flash("You cannot edit this user's GM Level", "warning") | ||||
|         return redirect(request.referrer if request.referrer else url_for("main.index")) | ||||
| @@ -138,10 +137,10 @@ def get(): | ||||
|             View | ||||
|             </a> | ||||
|         """ | ||||
|             #        <a role="button" class="btn btn-danger btn btn-block" | ||||
|             # href='{url_for('acounts.delete', id=account["0"])}'> | ||||
|             # Delete | ||||
|             # </a> | ||||
|         #        <a role="button" class="btn btn-danger btn btn-block" | ||||
|         # href='{url_for('acounts.delete', id=account["0"])}'> | ||||
|         # Delete | ||||
|         # </a> | ||||
|  | ||||
|         if account["4"]: | ||||
|             account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' | ||||
| @@ -154,13 +153,13 @@ def get(): | ||||
|             account["5"] = '''<h2 class="far fa-check-square text-success"></h2>''' | ||||
|  | ||||
|         if account["6"]: | ||||
|             account["6"] = f'''<h2 class="far fa-times-circle text-danger"></h2>''' | ||||
|             account["6"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' | ||||
|         else: | ||||
|             account["6"] = '''<h2 class="far fa-check-square text-success"></h2>''' | ||||
|  | ||||
|         if current_app.config["USER_ENABLE_EMAIL"]: | ||||
|             if account["8"]: | ||||
|                 account["8"] = f'''<h2 class="far fa-check-square text-success"></h2>''' | ||||
|                 account["8"] = '''<h2 class="far fa-check-square text-success"></h2>''' | ||||
|             else: | ||||
|                 account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' | ||||
|         else: | ||||
| @@ -175,4 +174,3 @@ def get(): | ||||
|             del account["8"] | ||||
|  | ||||
|     return data | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, flash | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, flash | ||||
| from flask_user import login_required, current_user | ||||
| from app.models import db, BugReport, CharacterInfo | ||||
| from datatables import ColumnDT, DataTables | ||||
| @@ -8,6 +8,7 @@ from app.luclient import translate_from_locale | ||||
|  | ||||
| bug_report_blueprint = Blueprint('bug_reports', __name__) | ||||
|  | ||||
|  | ||||
| @bug_report_blueprint.route('/<status>', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -23,7 +24,7 @@ def view(id): | ||||
|     if report.resoleved_by: | ||||
|         rb = report.resoleved_by.username | ||||
|     else: | ||||
|         rb="" | ||||
|         rb = "" | ||||
|     return render_template('bug_reports/view.html.j2', report=report, resolved_by=rb) | ||||
|  | ||||
|  | ||||
| @@ -62,12 +63,12 @@ def get(status): | ||||
|     ] | ||||
|  | ||||
|     query = None | ||||
|     if status=="all": | ||||
|     if status == "all": | ||||
|         query = db.session.query().select_from(BugReport) | ||||
|     elif status=="resolved": | ||||
|         query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time != None) | ||||
|     elif status=="unresolved": | ||||
|         query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time == None) | ||||
|     elif status == "resolved": | ||||
|         query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time not None) | ||||
|     elif status == "unresolved": | ||||
|         query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time is None) | ||||
|     else: | ||||
|         raise Exception("Not a valid filter") | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response | ||||
| from flask_user import login_required, current_user | ||||
| import json | ||||
| from datatables import ColumnDT, DataTables | ||||
| import datetime, time | ||||
| import time | ||||
| from app.models import CharacterInfo, CharacterXML, Account, db | ||||
| from app.schemas import CharacterInfoSchema | ||||
| from app.forms import RescueForm | ||||
| @@ -16,6 +15,7 @@ character_blueprint = Blueprint('characters', __name__) | ||||
|  | ||||
| character_schema = CharacterInfoSchema() | ||||
|  | ||||
|  | ||||
| @character_blueprint.route('/', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -27,7 +27,7 @@ def index(): | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| def approve_name(id, action): | ||||
|     character =  CharacterInfo.query.filter(CharacterInfo.id == id).first() | ||||
|     character = CharacterInfo.query.filter(CharacterInfo.id == id).first() | ||||
|  | ||||
|     if action == "approve": | ||||
|         log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}") | ||||
| @@ -42,9 +42,12 @@ def approve_name(id, action): | ||||
|  | ||||
|     elif action == "rename": | ||||
|         character.needs_rename = True | ||||
|         log_audit(f"Marked character ({character.id}){character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename") | ||||
|         log_audit( | ||||
|             f"Marked character ({character.id}){character.name} \ | ||||
|                 (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename") | ||||
|         flash( | ||||
|             f"Marked character {character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename", | ||||
|             f"Marked character {character.name} \ | ||||
|                 (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename", | ||||
|             "danger" | ||||
|         ) | ||||
|  | ||||
| @@ -67,11 +70,11 @@ def view(id): | ||||
|             abort(403) | ||||
|             return | ||||
|     character_json = xmltodict.parse( | ||||
|             CharacterXML.query.filter( | ||||
|                 CharacterXML.id==id | ||||
|             ).first().xml_data, | ||||
|             attr_prefix="attr_" | ||||
|         ) | ||||
|         CharacterXML.query.filter( | ||||
|             CharacterXML.id == id | ||||
|         ).first().xml_data, | ||||
|         attr_prefix="attr_" | ||||
|     ) | ||||
|  | ||||
|     # print json for reference | ||||
|     # with open("errorchar.json", "a") as file: | ||||
| @@ -84,8 +87,7 @@ def view(id): | ||||
|     # sort by items slot index | ||||
|     for inv in character_json["obj"]["inv"]["holdings"]["in"]: | ||||
|         if "i" in inv.keys() and type(inv["i"]) == list: | ||||
|             inv["i"] = sorted(inv["i"], key = lambda i: int(i['attr_s'])) | ||||
|  | ||||
|             inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s'])) | ||||
|  | ||||
|     return render_template( | ||||
|         'character/view.html.j2', | ||||
| @@ -110,13 +112,14 @@ def view_xml(id): | ||||
|             return | ||||
|  | ||||
|     character_xml = CharacterXML.query.filter( | ||||
|                         CharacterXML.id==id | ||||
|                     ).first().xml_data | ||||
|         CharacterXML.id == id | ||||
|     ).first().xml_data | ||||
|  | ||||
|     response = make_response(character_xml) | ||||
|     response.headers.set('Content-Type', 'text/xml') | ||||
|     return response | ||||
|  | ||||
|  | ||||
| @character_blueprint.route('/get_xml/<id>', methods=['GET']) | ||||
| @login_required | ||||
| def get_xml(id): | ||||
| @@ -132,8 +135,8 @@ def get_xml(id): | ||||
|             return | ||||
|  | ||||
|     character_xml = CharacterXML.query.filter( | ||||
|                         CharacterXML.id==id | ||||
|                     ).first().xml_data | ||||
|         CharacterXML.id == id | ||||
|     ).first().xml_data | ||||
|  | ||||
|     response = make_response(character_xml) | ||||
|     response.headers.set('Content-Type', 'attachment/xml') | ||||
| @@ -144,6 +147,7 @@ def get_xml(id): | ||||
|     ) | ||||
|     return response | ||||
|  | ||||
|  | ||||
| @character_blueprint.route('/restrict/<bit>/<id>', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -177,8 +181,8 @@ def rescue(id): | ||||
|     form = RescueForm() | ||||
|  | ||||
|     character_data = CharacterXML.query.filter( | ||||
|                         CharacterXML.id==id | ||||
|                     ).first() | ||||
|         CharacterXML.id == id | ||||
|     ).first() | ||||
|  | ||||
|     character_xml = ET.XML(character_data.xml_data) | ||||
|     for zone in character_xml.findall('.//r'): | ||||
| @@ -205,6 +209,7 @@ def rescue(id): | ||||
|  | ||||
|     return render_template("character/rescue.html.j2", form=form) | ||||
|  | ||||
|  | ||||
| @character_blueprint.route('/get/<status>', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -220,12 +225,12 @@ def get(status): | ||||
|     ] | ||||
|  | ||||
|     query = None | ||||
|     if status=="all": | ||||
|     if status == "all": | ||||
|         query = db.session.query().select_from(CharacterInfo).join(Account) | ||||
|     elif status=="approved": | ||||
|         query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename == False)) | ||||
|     elif status=="unapproved": | ||||
|         query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename == True)) | ||||
|     elif status == "approved": | ||||
|         query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename is False)) | ||||
|     elif status == "unapproved": | ||||
|         query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename is True)) | ||||
|     else: | ||||
|         raise Exception("Not a valid filter") | ||||
|  | ||||
| @@ -286,6 +291,4 @@ def get(status): | ||||
|         if perm_map & (1 << 6): | ||||
|             character["6"] += "Restricted Chat</br>" | ||||
|  | ||||
|  | ||||
|     return data | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,20 @@ | ||||
| import click | ||||
| import json | ||||
| from flask.cli import with_appcontext | ||||
| import random, string, datetime | ||||
| import random | ||||
| import string | ||||
| import datetime | ||||
| from flask_user import current_app | ||||
| from app import db | ||||
| from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC | ||||
| import pathlib | ||||
| import zlib | ||||
| import os | ||||
| from wand import image | ||||
| from wand.exceptions import BlobError as BE | ||||
| import app.pylddlib as ldd | ||||
| from multiprocessing import Pool | ||||
| from functools import partial | ||||
|  | ||||
|  | ||||
| @click.command("init_db") | ||||
| @click.argument('drop_tables', nargs=1) | ||||
| @with_appcontext | ||||
| @@ -45,6 +46,7 @@ def init_accounts(): | ||||
|  | ||||
|     return | ||||
|  | ||||
|  | ||||
| @click.command("load_property") | ||||
| @click.argument('zone') | ||||
| @click.argument('player') | ||||
| @@ -56,7 +58,7 @@ def load_property(zone, player): | ||||
|         print("Character not Found") | ||||
|         return 404 | ||||
|  | ||||
|     prop = Property.query.filter(Property.owner_id==char.id).filter(Property.zone_id==zone).first() | ||||
|     prop = Property.query.filter(Property.owner_id == char.id).filter(Property.zone_id == zone).first() | ||||
|  | ||||
|     if not prop: | ||||
|         print(f"Property {zone} not claimed by Character: {char.name}") | ||||
| @@ -95,6 +97,7 @@ def load_property(zone, player): | ||||
|             ) | ||||
|             new_prop_content.save() | ||||
|  | ||||
|  | ||||
| @click.command("gen_image_cache") | ||||
| def gen_image_cache(): | ||||
|     """generates image cache""" | ||||
| @@ -113,6 +116,7 @@ def gen_image_cache(): | ||||
|             except BE: | ||||
|                 return print(f"Error on {file}") | ||||
|  | ||||
|  | ||||
| @click.command("gen_model_cache") | ||||
| def gen_model_cache(): | ||||
|     """generate model obj cache""" | ||||
| @@ -123,19 +127,21 @@ def gen_model_cache(): | ||||
|     pool.map(partial(convert_lxfml_to_obj, lod=1), files) | ||||
|     pool.map(partial(convert_lxfml_to_obj, lod=2), files) | ||||
|  | ||||
|  | ||||
| def convert_lxfml_to_obj(file, lod): | ||||
|     mtl = get_cache_file(file).with_suffix(f".lod{lod}.mtl") | ||||
|     if not mtl.exists(): | ||||
|         mtl.parent.mkdir(parents=True, exist_ok=True) | ||||
|         print(f"Convert LXFML {file.as_posix()} to obj and mtl @ {mtl}") | ||||
|         try: | ||||
|             ldd.main(str(file.as_posix()), str(mtl.with_suffix("").as_posix()), lod) # convert to OBJ | ||||
|             ldd.main(str(file.as_posix()), str(mtl.with_suffix("").as_posix()), lod)  # convert to OBJ | ||||
|         except Exception as e: | ||||
|             print(f"ERROR on {file}:\n {e}") | ||||
|     else: | ||||
|         # print(f"Already Exists: {file} with LOD {lod}") | ||||
|         return | ||||
|  | ||||
|  | ||||
| def get_cache_file(path): | ||||
|     """helper""" | ||||
|     # convert to list so that we can change elements | ||||
| @@ -147,6 +153,7 @@ def get_cache_file(path): | ||||
|  | ||||
|     return pathlib.Path(*parts) | ||||
|  | ||||
|  | ||||
| def find_or_create_account(name, email, password, gm_level=9): | ||||
|     """ Find existing account or create new account """ | ||||
|     account = Account.query.filter(Account.email == email).first() | ||||
| @@ -164,15 +171,16 @@ def find_or_create_account(name, email, password, gm_level=9): | ||||
|         db.session.commit() | ||||
|  | ||||
|         play_key = PlayKey.query.filter(PlayKey.key_string == key).first() | ||||
|         account = Account(email=email, | ||||
|                     username=name, | ||||
|                     password=current_app.user_manager.password_manager.hash_password(password), | ||||
|                     play_key_id=play_key.id, | ||||
|                     email_confirmed_at=datetime.datetime.utcnow(), | ||||
|                     gm_level=gm_level | ||||
|                 ) | ||||
|         account = Account( | ||||
|             email=email, | ||||
|             username=name, | ||||
|             password=current_app.user_manager.password_manager.hash_password(password), | ||||
|             play_key_id=play_key.id, | ||||
|             email_confirmed_at=datetime.datetime.utcnow(), | ||||
|             gm_level=gm_level | ||||
|         ) | ||||
|         play_key.key_uses = 0 | ||||
|         db.session.add(account) | ||||
|         db.session.add(play_key) | ||||
|         db.session.commit() | ||||
|     return # account | ||||
|     return  # account | ||||
|   | ||||
							
								
								
									
										14
									
								
								app/forms.py
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								app/forms.py
									
									
									
									
									
								
							| @@ -16,13 +16,13 @@ from wtforms import ( | ||||
|     SubmitField, | ||||
|     validators, | ||||
|     IntegerField, | ||||
|     StringField, | ||||
|     SelectField | ||||
| ) | ||||
|  | ||||
| from wtforms.validators import DataRequired, Optional | ||||
| from app.models import PlayKey | ||||
|  | ||||
|  | ||||
| def validate_play_key(form, field): | ||||
|     """Validates a field for a valid phone number | ||||
|     Args: | ||||
| @@ -85,6 +85,7 @@ class CustomRegisterForm(FlaskForm): | ||||
|  | ||||
|     submit = SubmitField('Register') | ||||
|  | ||||
|  | ||||
| class CreatePlayKeyForm(FlaskForm): | ||||
|  | ||||
|     count = IntegerField( | ||||
| @@ -97,6 +98,7 @@ class CreatePlayKeyForm(FlaskForm): | ||||
|     ) | ||||
|     submit = SubmitField('Create!') | ||||
|  | ||||
|  | ||||
| class EditPlayKeyForm(FlaskForm): | ||||
|  | ||||
|     active = BooleanField( | ||||
| @@ -119,7 +121,7 @@ class EditGMLevelForm(FlaskForm): | ||||
|  | ||||
|     gm_level = IntegerField( | ||||
|         'GM Level', | ||||
|         widget=NumberInput(min = 0, max = 9) | ||||
|         widget=NumberInput(min=0, max=9) | ||||
|     ) | ||||
|  | ||||
|     submit = SubmitField('Submit') | ||||
| @@ -142,8 +144,8 @@ class SendMailForm(FlaskForm): | ||||
|         'Recipient: ', | ||||
|         coerce=str, | ||||
|         choices=[ | ||||
|             ("",""), | ||||
|             ("0","All Characters"), | ||||
|             ("", ""), | ||||
|             ("0", "All Characters"), | ||||
|         ], | ||||
|         validators=[validators.DataRequired()] | ||||
|     ) | ||||
| @@ -162,7 +164,7 @@ class SendMailForm(FlaskForm): | ||||
|     attachment = SelectField( | ||||
|         "Attachment", | ||||
|         coerce=str, | ||||
|         choices=[(0,"No Attachment")] | ||||
|         choices=[(0, "No Attachment")] | ||||
|     ) | ||||
|  | ||||
|     attachment_count = IntegerField( | ||||
| @@ -179,7 +181,7 @@ class RescueForm(FlaskForm): | ||||
|         'Move to:', | ||||
|         coerce=str, | ||||
|         choices=[ | ||||
|             ("",""), | ||||
|             ("", ""), | ||||
|         ], | ||||
|         validators=[validators.DataRequired()] | ||||
|     ) | ||||
|   | ||||
							
								
								
									
										19
									
								
								app/log.py
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								app/log.py
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| from flask import render_template, Blueprint, request, url_for | ||||
| from flask_user import login_required, current_user | ||||
| from flask_user import login_required | ||||
| from app.models import CommandLog, ActivityLog, db, Account, CharacterInfo, AuditLog | ||||
| from datatables import ColumnDT, DataTables | ||||
| import time | ||||
| @@ -7,6 +7,7 @@ from app import gm_level | ||||
|  | ||||
| log_blueprint = Blueprint('log', __name__) | ||||
|  | ||||
|  | ||||
| @log_blueprint.route('/activities', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(8) | ||||
| @@ -40,11 +41,11 @@ def audit(): | ||||
| @gm_level(8) | ||||
| def get_activities(): | ||||
|     columns = [ | ||||
|         ColumnDT(ActivityLog.id),           # 0 | ||||
|         ColumnDT(ActivityLog.character_id), # 1 | ||||
|         ColumnDT(ActivityLog.activity),     # 2 | ||||
|         ColumnDT(ActivityLog.time),         # 3 | ||||
|         ColumnDT(ActivityLog.map_id),       # 4 | ||||
|         ColumnDT(ActivityLog.id),               # 0 | ||||
|         ColumnDT(ActivityLog.character_id),     # 1 | ||||
|         ColumnDT(ActivityLog.activity),         # 2 | ||||
|         ColumnDT(ActivityLog.time),             # 3 | ||||
|         ColumnDT(ActivityLog.map_id),           # 4 | ||||
|     ] | ||||
|  | ||||
|     query = db.session.query().select_from(ActivityLog) | ||||
| @@ -82,9 +83,9 @@ def get_activities(): | ||||
| @gm_level(8) | ||||
| def get_commands(): | ||||
|     columns = [ | ||||
|         ColumnDT(CommandLog.id),           # 0 | ||||
|         ColumnDT(CommandLog.character_id), # 1 | ||||
|         ColumnDT(CommandLog.command),      # 2 | ||||
|         ColumnDT(CommandLog.id),            # 0 | ||||
|         ColumnDT(CommandLog.character_id),  # 1 | ||||
|         ColumnDT(CommandLog.command),       # 2 | ||||
|     ] | ||||
|  | ||||
|     query = db.session.query().select_from(CommandLog) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ import xml.etree.ElementTree as ET | ||||
| luclient_blueprint = Blueprint('luclient', __name__) | ||||
| locale = {} | ||||
|  | ||||
|  | ||||
| @luclient_blueprint.route('/get_dds_as_png/<filename>') | ||||
| @login_required | ||||
| def get_dds_as_png(filename): | ||||
| @@ -37,7 +38,7 @@ def get_dds_as_png(filename): | ||||
|  | ||||
|         with image.Image(filename=path) as img: | ||||
|             img.compression = "no" | ||||
|             img.save(filename='app/cache/'+filename.split('.')[0] + '.png') | ||||
|             img.save(filename='app/cache/' + filename.split('.')[0] + '.png') | ||||
|  | ||||
|     return send_file(cache) | ||||
|  | ||||
| @@ -69,7 +70,8 @@ def get_icon_lot(id): | ||||
|     )[0] | ||||
|  | ||||
|     # find the asset from rendercomponent given the  component id | ||||
|     filename = query_cdclient('select icon_asset from RenderComponent where id = ?', | ||||
|     filename = query_cdclient( | ||||
|         'select icon_asset from RenderComponent where id = ?', | ||||
|         [render_component_id], | ||||
|         one=True | ||||
|     )[0] | ||||
| @@ -117,6 +119,7 @@ def get_icon_iconid(id): | ||||
|  | ||||
|     return send_file(pathlib.Path(cache).resolve()) | ||||
|  | ||||
|  | ||||
| @luclient_blueprint.route('/unknown') | ||||
| @login_required | ||||
| def unknown(): | ||||
| @@ -195,6 +198,7 @@ def translate_from_locale(trans_string): | ||||
|     else: | ||||
|         return trans_string | ||||
|  | ||||
|  | ||||
| def register_luclient_jinja_helpers(app): | ||||
|  | ||||
|     @app.template_filter('get_zone_name') | ||||
| @@ -203,15 +207,17 @@ def register_luclient_jinja_helpers(app): | ||||
|  | ||||
|     @app.template_filter('get_skill_desc') | ||||
|     def get_skill_desc(skill_id): | ||||
|         return translate_from_locale(f'SkillBehavior_{skill_id}_descriptionUI').replace( | ||||
|                 "%(DamageCombo)", "Damage Combo: " | ||||
|             ).replace( | ||||
|                 "%(AltCombo)", "<br/>Skeleton Combo: " | ||||
|             ).replace( | ||||
|                 "%(Description)", "<br/>" | ||||
|             ).replace( | ||||
|                 "%(ChargeUp)", "<br/>Charge-up: " | ||||
|             ) | ||||
|         return translate_from_locale( | ||||
|             f'SkillBehavior_{skill_id}_descriptionUI' | ||||
|         ).replace( | ||||
|             "%(DamageCombo)", "Damage Combo: " | ||||
|         ).replace( | ||||
|             "%(AltCombo)", "<br/>Skeleton Combo: " | ||||
|         ).replace( | ||||
|             "%(Description)", "<br/>" | ||||
|         ).replace( | ||||
|             "%(ChargeUp)", "<br/>Charge-up: " | ||||
|         ) | ||||
|  | ||||
|     @app.template_filter('parse_lzid') | ||||
|     def parse_lzid(lzid): | ||||
| @@ -240,7 +246,7 @@ def register_luclient_jinja_helpers(app): | ||||
|                 one=True | ||||
|             ) | ||||
|             if intermed: | ||||
|                 name = intermed[7] if (intermed[7] != "None" and intermed[7] !="" and intermed[7] != None) else intermed[1] | ||||
|                 name = intermed[7] if (intermed[7] != "None" and intermed[7] != "" and intermed[7] is None) else intermed[1] | ||||
|         return name | ||||
|  | ||||
|     @app.template_filter('get_lot_rarity') | ||||
| @@ -252,7 +258,8 @@ def register_luclient_jinja_helpers(app): | ||||
|             one=True | ||||
|         )[0] | ||||
|  | ||||
|         rarity = query_cdclient('select rarity from ItemComponent where id = ?', | ||||
|         rarity = query_cdclient( | ||||
|             'select rarity from ItemComponent where id = ?', | ||||
|             [render_component_id], | ||||
|             one=True | ||||
|         ) | ||||
| @@ -302,7 +309,6 @@ def register_luclient_jinja_helpers(app): | ||||
|  | ||||
|         return consolidate_stats(stats) | ||||
|  | ||||
|  | ||||
|     @app.template_filter('get_set_stats') | ||||
|     def get_set_stats(lot_id): | ||||
|         stats = query_cdclient( | ||||
| @@ -331,7 +337,7 @@ def register_luclient_jinja_helpers(app): | ||||
| def consolidate_stats(stats): | ||||
|  | ||||
|     if len(stats) > 1: | ||||
|         consolidated_stats = {"im": 0,"life": 0,"armor": 0, "skill": []} | ||||
|         consolidated_stats = {"im": 0, "life": 0, "armor": 0, "skill": []} | ||||
|         for stat in stats: | ||||
|             if stat[0]: | ||||
|                 consolidated_stats["im"] += stat[0] | ||||
| @@ -340,8 +346,7 @@ def consolidate_stats(stats): | ||||
|             if stat[2]: | ||||
|                 consolidated_stats["armor"] += stat[2] | ||||
|             if stat[3]: | ||||
|                 consolidated_stats["skill"].append([stat[3],stat[4]]) | ||||
|  | ||||
|                 consolidated_stats["skill"].append([stat[3], stat[4]]) | ||||
|  | ||||
|         stats = consolidated_stats | ||||
|     elif len(stats) == 1: | ||||
|   | ||||
							
								
								
									
										56
									
								
								app/mail.py
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								app/mail.py
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, request | ||||
| from flask import render_template, Blueprint, redirect, url_for, flash, request | ||||
| from flask_user import login_required, current_user | ||||
| from app.models import db, Mail, CharacterInfo | ||||
| from datatables import ColumnDT, DataTables | ||||
| from app.models import Mail, CharacterInfo | ||||
| from app.forms import SendMailForm | ||||
| from app import gm_level, log_audit | ||||
| from app.luclient import translate_from_locale, query_cdclient | ||||
| @@ -31,37 +30,40 @@ def send(): | ||||
|             log_audit(f"Sending {form.subject.data}: {form.body.data} to All Characters with {form.attachment_count.data} of item {form.attachment.data}") | ||||
|             for character in CharacterInfo.query.all(): | ||||
|                 Mail( | ||||
|                     sender_id = 0, | ||||
|                     sender_name = f"[GM] {current_user.username}", | ||||
|                     receiver_id = character.id, | ||||
|                     receiver_name = character.name, | ||||
|                     time_sent = time.time(), | ||||
|                     subject = form.subject.data, | ||||
|                     body = form.body.data, | ||||
|                     attachment_id = 0, | ||||
|                     attachment_lot = form.attachment.data, | ||||
|                     attachment_count = form.attachment_count.data | ||||
|                     sender_id=0, | ||||
|                     sender_name=f"[GM] {current_user.username}", | ||||
|                     receiver_id=character.id, | ||||
|                     receiver_name=character.name, | ||||
|                     time_sent=time.time(), | ||||
|                     subject=form.subject.data, | ||||
|                     body=form.body.data, | ||||
|                     attachment_id=0, | ||||
|                     attachment_lot=form.attachment.data, | ||||
|                     attachment_count=form.attachment_count.data | ||||
|                 ).save() | ||||
|                 log_audit(f"Sent {form.subject.data}: {form.body.data} to ({character.id}){character.name} with {form.attachment_count.data} of item {form.attachment.data}") | ||||
|                 log_audit(f"Sent {form.subject.data}: \ | ||||
|                     {form.body.data} to ({character.id}){character.name} \ | ||||
|                     with {form.attachment_count.data} of item {form.attachment.data}") | ||||
|         else: | ||||
|             Mail( | ||||
|                 sender_id = 0, | ||||
|                 sender_name = f"[GM] {current_user.username}", | ||||
|                 receiver_id = form.recipient.data, | ||||
|                 receiver_name = CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name, | ||||
|                 time_sent = time.time(), | ||||
|                 subject = form.subject.data, | ||||
|                 body = form.body.data, | ||||
|                 attachment_id = 0, | ||||
|                 attachment_lot = form.attachment.data, | ||||
|                 attachment_count = form.attachment_count.data | ||||
|                 sender_id=0, | ||||
|                 sender_name=f"[GM] {current_user.username}", | ||||
|                 receiver_id=form.recipient.data, | ||||
|                 receiver_name=CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name, | ||||
|                 time_sent=time.time(), | ||||
|                 subject=form.subject.data, | ||||
|                 body=form.body.data, | ||||
|                 attachment_id=0, | ||||
|                 attachment_lot=form.attachment.data, | ||||
|                 attachment_count=form.attachment_count.data | ||||
|             ).save() | ||||
|             log_audit(f"Sent {form.subject.data}: {form.body.data} to ({form.recipient.data}){CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name} with {form.attachment_count.data} of item {form.attachment.data}") | ||||
|             log_audit(f"Sent {form.subject.data}: \ | ||||
|                 {form.body.data} to ({form.recipient.data}){CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name} \ | ||||
|                 with {form.attachment_count.data} of item {form.attachment.data}") | ||||
|  | ||||
|         flash("Sent Mail", "success") | ||||
|         return redirect(url_for('mail.send')) | ||||
|  | ||||
|  | ||||
|     recipients = CharacterInfo.query.all() | ||||
|     for character in recipients: | ||||
|         form.recipient.choices.append((character.id, character.name)) | ||||
| @@ -74,7 +76,7 @@ def send(): | ||||
|     for item in items: | ||||
|         name = translate_from_locale(f'Objects_{item[0]}_name') | ||||
|         if name == f'Objects_{item[0]}_name': | ||||
|             name = (item[2] if (item[2] != "None" and item[2] !="" and item[2] != None) else item[1]) | ||||
|             name = (item[2] if (item[2] != "None" and item[2] != "" and item[2] not None) else item[1]) | ||||
|         form.attachment.choices.append( | ||||
|             ( | ||||
|                 item[0], | ||||
|   | ||||
							
								
								
									
										11
									
								
								app/main.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								app/main.py
									
									
									
									
									
								
							| @@ -1,18 +1,15 @@ | ||||
| from flask import render_template, Blueprint, redirect,  request, send_from_directory, make_response, send_file, current_app | ||||
| from flask_user import login_required, current_user | ||||
| import json, glob, os | ||||
| from wand import image | ||||
| from flask import render_template, Blueprint, send_from_directory | ||||
| from flask_user import current_user | ||||
|  | ||||
| from app.models import Account, AccountInvitation, CharacterInfo | ||||
| from app.models import Account | ||||
| from app.schemas import AccountSchema, CharacterInfoSchema | ||||
| from app.luclient import query_cdclient | ||||
| from app import gm_level | ||||
|  | ||||
| main_blueprint = Blueprint('main', __name__) | ||||
|  | ||||
| account_schema = AccountSchema() | ||||
| char_info_schema = CharacterInfoSchema() | ||||
|  | ||||
|  | ||||
| @main_blueprint.route('/', methods=['GET']) | ||||
| def index(): | ||||
|     """Home/Index Page""" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| from flask_sqlalchemy import SQLAlchemy | ||||
| from flask_migrate import Migrate | ||||
| from flask_user import UserMixin, current_user | ||||
| from flask_user import UserMixin | ||||
| from wtforms import ValidationError | ||||
|  | ||||
| import logging | ||||
| @@ -12,6 +12,7 @@ from time import sleep | ||||
| import random | ||||
| import string | ||||
|  | ||||
|  | ||||
| # retrying query to work around python trash collector | ||||
| # killing connections of other gunicorn workers | ||||
| class RetryingQuery(BaseQuery): | ||||
| @@ -46,9 +47,11 @@ class RetryingQuery(BaseQuery): | ||||
|                     raise | ||||
|                 self.session.rollback() | ||||
|  | ||||
|  | ||||
| db = SQLAlchemy(query_class=RetryingQuery) | ||||
| migrate = Migrate() | ||||
|  | ||||
|  | ||||
| class PlayKey(db.Model): | ||||
|     __tablename__ = 'play_keys' | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
| @@ -121,6 +124,7 @@ class PlayKey(db.Model): | ||||
|         db.session.commit() | ||||
|         db.session.refresh(self) | ||||
|  | ||||
|  | ||||
| class Account(db.Model, UserMixin): | ||||
|     __tablename__ = 'accounts' | ||||
|     id = db.Column( | ||||
| @@ -200,7 +204,7 @@ class Account(db.Model, UserMixin): | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_user_by_id(*, user_id=None): | ||||
|         return User.query.filter(user_id == User.id).first() | ||||
|         return Account.query.filter(user_id == Account.id).first() | ||||
|  | ||||
|     def save(self): | ||||
|         db.session.add(self) | ||||
| @@ -211,6 +215,7 @@ class Account(db.Model, UserMixin): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class AccountInvitation(db.Model): | ||||
|     __tablename__ = 'account_invites' | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
| @@ -245,14 +250,10 @@ class AccountInvitation(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_user_by_id(*, user_id=None): | ||||
|         return User.query.filter(user_id == User.id).first() | ||||
|         return Account.query.filter(user_id == Account.id).first() | ||||
|  | ||||
|     def delete(self): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
| # This table is cursed, see prop_clone_id | ||||
| class CharacterInfo(db.Model): | ||||
| @@ -327,6 +328,7 @@ class CharacterInfo(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class CharacterXML(db.Model): | ||||
|     __tablename__ = 'charxml' | ||||
|     id = db.Column( | ||||
| @@ -348,6 +350,7 @@ class CharacterXML(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class CommandLog(db.Model): | ||||
|     __tablename__ = 'command_log' | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
| @@ -364,7 +367,7 @@ class CommandLog(db.Model): | ||||
|         passive_deletes=True | ||||
|     ) | ||||
|  | ||||
|     command =  db.Column( | ||||
|     command = db.Column( | ||||
|         mysql.VARCHAR(256), | ||||
|         nullable=False | ||||
|     ) | ||||
| @@ -378,6 +381,7 @@ class CommandLog(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class Friends(db.Model): | ||||
|     __tablename__ = 'friends' | ||||
|     player_id = db.Column( | ||||
| @@ -423,6 +427,7 @@ class Friends(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class Leaderboard(db.Model): | ||||
|     __tablename__ = 'leaderboard' | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
| @@ -472,6 +477,7 @@ class Leaderboard(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class Mail(db.Model): | ||||
|     __tablename__ = 'mail' | ||||
|     id = db.Column( | ||||
| @@ -560,6 +566,7 @@ class Mail(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class ObjectIDTracker(db.Model): | ||||
|     __tablename__ = 'object_id_tracker' | ||||
|     last_object_id = db.Column( | ||||
| @@ -569,6 +576,7 @@ class ObjectIDTracker(db.Model): | ||||
|         server_default='0' | ||||
|     ) | ||||
|  | ||||
|  | ||||
| class PetNames(db.Model): | ||||
|     __tablename__ = 'pet_names' | ||||
|     id = db.Column(mysql.BIGINT, primary_key=True) | ||||
| @@ -582,7 +590,7 @@ class PetNames(db.Model): | ||||
|         server_default='0' | ||||
|     ) | ||||
|  | ||||
|     owner_id  = db.Column( | ||||
|     owner_id = db.Column( | ||||
|         mysql.BIGINT, | ||||
|         nullable=True | ||||
|     ) | ||||
| @@ -605,7 +613,7 @@ class Property(db.Model): | ||||
|         autoincrement=False | ||||
|     ) | ||||
|  | ||||
|     owner_id  = db.Column( | ||||
|     owner_id = db.Column( | ||||
|         mysql.BIGINT, | ||||
|         db.ForeignKey(CharacterInfo.id, ondelete='CASCADE'), | ||||
|         nullable=False | ||||
| @@ -623,7 +631,7 @@ class Property(db.Model): | ||||
|         nullable=False, | ||||
|     ) | ||||
|  | ||||
|     clone_id  = db.Column( | ||||
|     clone_id = db.Column( | ||||
|         mysql.BIGINT(unsigned=True), | ||||
|         db.ForeignKey(CharacterInfo.prop_clone_id, ondelete='CASCADE'), | ||||
|     ) | ||||
| @@ -763,6 +771,7 @@ class UGC(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class PropertyContent(db.Model): | ||||
|     __tablename__ = 'properties_contents' | ||||
|     id = db.Column( | ||||
| @@ -843,6 +852,7 @@ class PropertyContent(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class ActivityLog(db.Model): | ||||
|     __tablename__ = 'activity_log' | ||||
|     id = db.Column(mysql.INTEGER, primary_key=True) | ||||
| @@ -883,6 +893,7 @@ class ActivityLog(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class BugReport(db.Model): | ||||
|     __tablename__ = 'bug_reports' | ||||
|     id = db.Column(mysql.INTEGER, primary_key=True) | ||||
| @@ -944,6 +955,7 @@ class BugReport(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class Server(db.Model): | ||||
|     __tablename__ = 'servers' | ||||
|     id = db.Column( | ||||
| @@ -1016,6 +1028,7 @@ class Reports(db.Model): | ||||
|         db.session.delete(self) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| class AuditLog(db.Model): | ||||
|     __tablename__ = 'audit_logs' | ||||
|     id = db.Column( | ||||
|   | ||||
| @@ -2,7 +2,6 @@ from flask import render_template, Blueprint, redirect, url_for, request, flash, | ||||
| from flask_user import login_required | ||||
| from app.models import PetNames, db, CharacterXML, CharacterInfo | ||||
| from datatables import ColumnDT, DataTables | ||||
| from app.forms import CreatePlayKeyForm, EditPlayKeyForm | ||||
| from app import gm_level, log_audit, scheduler | ||||
|  | ||||
| moderation_blueprint = Blueprint('moderation', __name__) | ||||
| @@ -20,7 +19,7 @@ def index(status): | ||||
| @gm_level(3) | ||||
| def approve_pet(id): | ||||
|  | ||||
|     pet_data =  PetNames.query.filter(PetNames.id == id).first() | ||||
|     pet_data = PetNames.query.filter(PetNames.id == id).first() | ||||
|  | ||||
|     pet_data.approved = 2 | ||||
|     log_audit(f"Approved pet name {pet_data.pet_name} from {pet_data.owner_id}") | ||||
| @@ -34,7 +33,7 @@ def approve_pet(id): | ||||
| @gm_level(3) | ||||
| def reject_pet(id): | ||||
|  | ||||
|     pet_data =  PetNames.query.filter(PetNames.id == id).first() | ||||
|     pet_data = PetNames.query.filter(PetNames.id == id).first() | ||||
|  | ||||
|     pet_data.approved = 0 | ||||
|     log_audit(f"Rejected pet name {pet_data.pet_name} from {pet_data.owner_id}") | ||||
| @@ -55,16 +54,15 @@ def get_pets(status="all"): | ||||
|     ] | ||||
|  | ||||
|     query = None | ||||
|     if status=="all": | ||||
|     if status == "all": | ||||
|         query = db.session.query().select_from(PetNames) | ||||
|     elif status=="approved": | ||||
|         query = db.session.query().select_from(PetNames).filter(PetNames.approved==2) | ||||
|     elif status=="unapproved": | ||||
|         query = db.session.query().select_from(PetNames).filter(PetNames.approved==1) | ||||
|     elif status == "approved": | ||||
|         query = db.session.query().select_from(PetNames).filter(PetNames.approved == 2) | ||||
|     elif status == "unapproved": | ||||
|         query = db.session.query().select_from(PetNames).filter(PetNames.approved == 1) | ||||
|     else: | ||||
|         raise Exception("Not a valid filter") | ||||
|  | ||||
|  | ||||
|     params = request.args.to_dict() | ||||
|  | ||||
|     rowTable = DataTables(params, query, columns) | ||||
| @@ -116,8 +114,8 @@ def get_pets(status="all"): | ||||
|                         {CharacterInfo.query.filter(CharacterInfo.id==pet_data['3']).first().name} | ||||
|                     </a> | ||||
|                 """ | ||||
|             except Exception as e: | ||||
|                 PetNames.query.filter(PetNames.id==id).first().delete() | ||||
|             except Exception: | ||||
|                 PetNames.query.filter(PetNames.id == id).first().delete() | ||||
|                 pet_data["0"] = "<span class='text-danger'>Deleted Refresh to make go away</span>" | ||||
|                 pet_data["3"] = "<span class='text-danger'>Character Deleted</span>" | ||||
|         else: | ||||
| @@ -131,7 +129,7 @@ def pet_name_maintenance(): | ||||
|     with scheduler.app.app_context(): | ||||
|         # associate pet names to characters | ||||
|         current_app.logger.info("Started Pet Name Maintenance") | ||||
|         unassociated_pets = PetNames.query.filter(PetNames.owner_id == None).all() | ||||
|         unassociated_pets = PetNames.query.filter(PetNames.owner_id is None).all() | ||||
|         if unassociated_pets: | ||||
|             current_app.logger.info("Found un-associated pets") | ||||
|             for pet in unassociated_pets: | ||||
| @@ -143,11 +141,11 @@ def pet_name_maintenance(): | ||||
|                     pet.delete() | ||||
|  | ||||
|         # auto-moderate based on already moderated names | ||||
|         unmoderated_pets = PetNames.query.filter(PetNames.approved==1).all() | ||||
|         unmoderated_pets = PetNames.query.filter(PetNames.approved == 1).all() | ||||
|         if unmoderated_pets: | ||||
|             current_app.logger.info("Found un-moderated Pets") | ||||
|             for pet in unmoderated_pets: | ||||
|                 existing_pet = PetNames.query.filter(PetNames.approved.in_([0,2])).filter(PetNames.pet_name == pet.pet_name).first() | ||||
|                 existing_pet = PetNames.query.filter(PetNames.approved.in_([0, 2])).filter(PetNames.pet_name == pet.pet_name).first() | ||||
|                 if existing_pet: | ||||
|                     pet.approved = existing_pet.approved | ||||
|                     pet.save() | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, flash | ||||
| from flask_user import login_required, current_user | ||||
| from app.models import Account, AccountInvitation, PlayKey, db | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, flash | ||||
| from flask_user import login_required | ||||
| from app.models import Account, PlayKey, db | ||||
| from datatables import ColumnDT, DataTables | ||||
| from app.forms import CreatePlayKeyForm, EditPlayKeyForm | ||||
| from app import gm_level, log_audit | ||||
|  | ||||
| play_keys_blueprint = Blueprint('play_keys', __name__) | ||||
|  | ||||
| # Key creation page | ||||
|  | ||||
| # Key creation page | ||||
| @play_keys_blueprint.route('/', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(9) | ||||
| @@ -45,7 +45,7 @@ def bulk_create(): | ||||
| @gm_level(9) | ||||
| def delete(id): | ||||
|     key = PlayKey.query.filter(PlayKey.id == id).first() | ||||
|     associated_accounts = Account.query.filter(Account.play_key_id==id).all() | ||||
|     # associated_accounts = Account.query.filter(Account.play_key_id==id).all() | ||||
|     log_audit(f"Deleted Play Key {key.key_string}") | ||||
|     flash(f"Deleted Play Key {key.key_string}", "danger") | ||||
|     key.delete() | ||||
| @@ -56,7 +56,7 @@ def delete(id): | ||||
| @login_required | ||||
| @gm_level(9) | ||||
| def edit(id): | ||||
|     key = PlayKey.query.filter(PlayKey.id==id).first() | ||||
|     key = PlayKey.query.filter(PlayKey.id == id).first() | ||||
|     form = EditPlayKeyForm() | ||||
|  | ||||
|     if form.validate_on_submit(): | ||||
| @@ -84,7 +84,7 @@ def edit(id): | ||||
| @gm_level(9) | ||||
| def view(id): | ||||
|     key = PlayKey.query.filter(PlayKey.id == id).first() | ||||
|     accounts = Account.query.filter(Account.play_key_id==id).all() | ||||
|     accounts = Account.query.filter(Account.play_key_id == id).all() | ||||
|     return render_template('play_keys/view.html.j2', key=key, accounts=accounts) | ||||
|  | ||||
|  | ||||
| @@ -125,7 +125,12 @@ def get(): | ||||
|                 Delete | ||||
|             </a> | ||||
|  | ||||
|             <div class="modal fade bd-example-modal-lg" id="delete-{play_key["1"]}-modal" tabindex="-1" role="dialog" aria-labelledby="delete-{play_key["1"]}-modalLabel" aria-hidden="true"> | ||||
|             <div class="modal | ||||
|                     fade bd-example-modal-lg" | ||||
|                     id="delete-{play_key["1"]}-modal" | ||||
|                     tabindex="-1" role="dialog" | ||||
|                     aria-labelledby="delete-{play_key["1"]}-modalLabel" | ||||
|                     aria-hidden="true"> | ||||
|                 <div class="modal-dialog modal-lg" role="document"> | ||||
|                 <div class="modal-content bg-dark border-primary"> | ||||
|                     <div class="modal-header"> | ||||
|   | ||||
| @@ -5,14 +5,11 @@ from flask import ( | ||||
|     url_for, | ||||
|     request, | ||||
|     abort, | ||||
|     jsonify, | ||||
|     send_from_directory, | ||||
|     make_response, | ||||
|     flash, | ||||
|     current_app | ||||
| ) | ||||
| from flask_user import login_required, current_user | ||||
| import json | ||||
| from datatables import ColumnDT, DataTables | ||||
| import time | ||||
| from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account | ||||
| @@ -21,8 +18,6 @@ from app import gm_level, log_audit | ||||
| from app.luclient import query_cdclient | ||||
|  | ||||
| import zlib | ||||
| import xmltodict | ||||
| import os | ||||
| import app.pylddlib as ldd | ||||
| import pathlib | ||||
|  | ||||
| @@ -30,6 +25,7 @@ property_blueprint = Blueprint('properties', __name__) | ||||
|  | ||||
| property_schema = PropertySchema() | ||||
|  | ||||
|  | ||||
| @property_blueprint.route('/', methods=['GET']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -42,7 +38,7 @@ def index(): | ||||
| @gm_level(3) | ||||
| def approve(id): | ||||
|  | ||||
|     property_data =  Property.query.filter(Property.id == id).first() | ||||
|     property_data = Property.query.filter(Property.id == id).first() | ||||
|  | ||||
|     property_data.mod_approved = not property_data.mod_approved | ||||
|  | ||||
| @@ -89,8 +85,6 @@ def approve(id): | ||||
|     else: | ||||
|         go_to = url_for('main.index') | ||||
|  | ||||
|  | ||||
|  | ||||
|     return redirect(go_to) | ||||
|  | ||||
|  | ||||
| @@ -134,16 +128,19 @@ def get(status="all"): | ||||
|     ] | ||||
|  | ||||
|     query = None | ||||
|     if status=="all": | ||||
|         query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account) | ||||
|     elif status=="approved": | ||||
|         query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account).filter(Property.mod_approved==True).filter(Property.privacy_option==2) | ||||
|     elif status=="unapproved": | ||||
|         query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account).filter(Property.mod_approved==False).filter(Property.privacy_option==2) | ||||
|     if status == "all": | ||||
|         query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id == Property.owner_id).join(Account) | ||||
|     elif status == "approved": | ||||
|         query = db.session.query().select_from(Property).join( | ||||
|             CharacterInfo, CharacterInfo.id == Property.owner_id | ||||
|         ).join(Account).filter(Property.mod_approved is True).filter(Property.privacy_option == 2) | ||||
|     elif status == "unapproved": | ||||
|         query = db.session.query().select_from(Property).join( | ||||
|             CharacterInfo, CharacterInfo.id == Property.owner_id | ||||
|         ).join(Account).filter(Property.mod_approved is False).filter(Property.privacy_option == 2) | ||||
|     else: | ||||
|         raise Exception("Not a valid filter") | ||||
|  | ||||
|  | ||||
|     params = request.args.to_dict() | ||||
|  | ||||
|     rowTable = DataTables(params, query, columns) | ||||
| @@ -183,10 +180,10 @@ def get(status="all"): | ||||
|  | ||||
|         if property_data["4"] == "": | ||||
|             property_data["4"] = query_cdclient( | ||||
|             'select DisplayDescription from ZoneTable where zoneID = ?', | ||||
|             [property_data["12"]], | ||||
|             one=True | ||||
|         ) | ||||
|                 'select DisplayDescription from ZoneTable where zoneID = ?', | ||||
|                 [property_data["12"]], | ||||
|                 one=True | ||||
|             ) | ||||
|  | ||||
|         if property_data["6"] == 0: | ||||
|             property_data["6"] = "Private" | ||||
| @@ -215,7 +212,7 @@ def get(status="all"): | ||||
| @property_blueprint.route('/view_model/<id>/<lod>', methods=['GET']) | ||||
| @login_required | ||||
| def view_model(id, lod): | ||||
|     property_content_data = PropertyContent.query.filter(PropertyContent.id==id).all() | ||||
|     property_content_data = PropertyContent.query.filter(PropertyContent.id == id).all() | ||||
|  | ||||
|     # TODO: Restrict somehow | ||||
|     formatted_data = [ | ||||
| @@ -242,6 +239,7 @@ def view_model(id, lod): | ||||
|         lod=lod | ||||
|     ) | ||||
|  | ||||
|  | ||||
| property_center = { | ||||
|     1150: "(-17, 432, -60)", | ||||
|     1151: "(0, 455, -110)", | ||||
| @@ -256,7 +254,7 @@ property_center = { | ||||
| @login_required | ||||
| def view_models(id, lod): | ||||
|     property_content_data = PropertyContent.query.filter( | ||||
|         PropertyContent.property_id==id | ||||
|         PropertyContent.property_id == id | ||||
|     ).order_by(PropertyContent.lot).all() | ||||
|  | ||||
|     consolidated_list = [] | ||||
| @@ -295,7 +293,7 @@ def view_models(id, lod): | ||||
|                     }] | ||||
|                 } | ||||
|             ) | ||||
|     property_data = Property.query.filter(Property.id==id).first() | ||||
|     property_data = Property.query.filter(Property.id == id).first() | ||||
|     return render_template( | ||||
|         'ldd/ldd.html.j2', | ||||
|         property_data=property_data, | ||||
| @@ -304,30 +302,30 @@ def view_models(id, lod): | ||||
|         lod=lod | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @property_blueprint.route('/get_model/<id>/<file_format>/<lod>', methods=['GET']) | ||||
| @login_required | ||||
| def get_model(id, file_format, lod): | ||||
|     content = PropertyContent.query.filter(PropertyContent.id==id).first() | ||||
|     content = PropertyContent.query.filter(PropertyContent.id == id).first() | ||||
|     if not(0 <= int(lod) <= 2): | ||||
|         abort(404) | ||||
|     if content.lot == 14: # ugc model | ||||
|     if content.lot == 14:  # ugc model | ||||
|         response = ugc(content)[0] | ||||
|     else: # prebuild model | ||||
|     else:  # prebuilt model | ||||
|         response = prebuilt(content, file_format, lod)[0] | ||||
|  | ||||
|     response.headers.set('Content-Type', 'text/xml') | ||||
|     return response | ||||
|  | ||||
|  | ||||
|  | ||||
| @property_blueprint.route('/download_model/<id>', methods=['GET']) | ||||
| @login_required | ||||
| def download_model(id): | ||||
|     content = PropertyContent.query.filter(PropertyContent.id==id).first() | ||||
|     content = PropertyContent.query.filter(PropertyContent.id == id).first() | ||||
|  | ||||
|     if content.lot == 14: # ugc model | ||||
|     if content.lot == 14:  # ugc model | ||||
|         response, filename = ugc(content) | ||||
|     else: # prebuild model | ||||
|     else:  # prebuilt model | ||||
|         response, filename = prebuilt(content, "lxfml") | ||||
|  | ||||
|     response.headers.set('Content-Type', 'attachment/xml') | ||||
| @@ -340,7 +338,7 @@ def download_model(id): | ||||
|  | ||||
|  | ||||
| def ugc(content): | ||||
|     ugc_data = UGC.query.filter(UGC.id==content.ugc_id).first() | ||||
|     ugc_data = UGC.query.filter(UGC.id == content.ugc_id).first() | ||||
|     uncompressed_lxfml = zlib.decompress(ugc_data.lxfml) | ||||
|     response = make_response(uncompressed_lxfml) | ||||
|     return response, ugc_data.filename | ||||
| @@ -355,7 +353,8 @@ def prebuilt(content, file_format, lod): | ||||
|         one=True | ||||
|     )[0] | ||||
|     # find the asset from rendercomponent given the  component id | ||||
|     filename = query_cdclient('select render_asset from RenderComponent where id = ?', | ||||
|     filename = query_cdclient( | ||||
|         'select render_asset from RenderComponent where id = ?', | ||||
|         [render_component_id], | ||||
|         one=True | ||||
|     ) | ||||
| @@ -375,7 +374,7 @@ def prebuilt(content, file_format, lod): | ||||
|         cache = pathlib.Path(f'app/cache/BrickModels/{filename}.lod{lod}.{file_format}') | ||||
|         cache.parent.mkdir(parents=True, exist_ok=True) | ||||
|         try: | ||||
|             ldd.main(str(lxfml.as_posix()), str(cache.with_suffix("").as_posix()), lod) # convert to OBJ | ||||
|             ldd.main(str(lxfml.as_posix()), str(cache.with_suffix("").as_posix()), lod)  # convert to OBJ | ||||
|         except Exception as e: | ||||
|             current_app.logger.error(f"ERROR on {cache}:\n {e}") | ||||
|  | ||||
|   | ||||
							
								
								
									
										361
									
								
								app/pylddlib.py
									
									
									
									
									
								
							
							
						
						
									
										361
									
								
								app/pylddlib.py
									
									
									
									
									
								
							| @@ -1,33 +1,12 @@ | ||||
| #!/usr/bin/env python | ||||
| # pylddlib version 0.4.9.7 | ||||
| # based on pyldd2obj version 0.4.8 - Copyright (c) 2019 by jonnysp | ||||
| # | ||||
| # Updates: | ||||
| # 0.4.9.8 Make work with LEGO Universe brickdb | ||||
| # 0.4.9.7 corrected bug of incorrectly parsing the primitive xml file, specifically with comments. Add support LDDLIFTREE envirnment variable to set location of db.lif. | ||||
| # 0.4.9.6 preliminary Linux support | ||||
| # 0.4.9.5 corrected bug of incorrectly Bounding / GeometryBounding parsing the primitive xml file. | ||||
| # 0.4.9.4 improved lif.db checking for crucial files (because of the infamous botched 4.3.12 LDD Windows update). | ||||
| # 0.4.9.3 improved Windows and Python 3 compatibility | ||||
| # 0.4.9.2 changed handling of material = 0 for a part. Now a 0 will choose the 1st material (the base material of a part) and not the previous material of the subpart before. This will fix "Chicken Helmet Part 11262". It may break other parts and this change needs further regression. | ||||
| # 0.4.9.1 improved custom2DField handling, fixed decorations bug, improved material assignments handling | ||||
| # 0.4.9 updates to support reading extracted db.lif from db folder | ||||
| # | ||||
| # License: MIT License | ||||
| # | ||||
|  | ||||
| import os | ||||
| import platform | ||||
| import sys | ||||
| import math | ||||
| import struct | ||||
| import zipfile | ||||
| from xml.dom import minidom | ||||
| import time | ||||
|  | ||||
| if sys.version_info < (3, 0): | ||||
|     reload(sys) | ||||
|     sys.setdefaultencoding('utf-8') | ||||
|  | ||||
| PRIMITIVEPATH = '/Primitives/' | ||||
| GEOMETRIEPATH = PRIMITIVEPATH | ||||
| @@ -36,8 +15,9 @@ MATERIALNAMESPATH = '/MaterialNames/' | ||||
|  | ||||
| LOGOONSTUDSCONNTYPE = {"0:4", "0:4:1", "0:4:2", "0:4:33", "2:4:1", "2:4:34"} | ||||
|  | ||||
|  | ||||
| class Matrix3D: | ||||
|     def __init__(self, n11=1,n12=0,n13=0,n14=0,n21=0,n22=1,n23=0,n24=0,n31=0,n32=0,n33=1,n34=0,n41=0,n42=0,n43=0,n44=1): | ||||
|     def __init__(self, n11=1, n12=0, n13=0, n14=0, n21=0, n22=1, n23=0, n24=0, n31=0, n32=0, n33=1, n34=0, n41=0, n42=0, n43=0, n44=1): | ||||
|         self.n11 = n11 | ||||
|         self.n12 = n12 | ||||
|         self.n13 = n13 | ||||
| @@ -56,9 +36,26 @@ class Matrix3D: | ||||
|         self.n44 = n44 | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}]'.format(self.n11, self.n12, self.n13,self.n14,self.n21, self.n22, self.n23,self.n24,self.n31, self.n32, self.n33,self.n34,self.n41, self.n42, self.n43,self.n44) | ||||
|         return '[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}]'.format( | ||||
|             self.n11, | ||||
|             self.n12, | ||||
|             self.n13, | ||||
|             self.n14, | ||||
|             self.n21, | ||||
|             self.n22, | ||||
|             self.n23, | ||||
|             self.n24, | ||||
|             self.n31, | ||||
|             self.n32, | ||||
|             self.n33, | ||||
|             self.n34, | ||||
|             self.n41, | ||||
|             self.n42, | ||||
|             self.n43, | ||||
|             self.n44 | ||||
|         ) | ||||
|  | ||||
|     def rotate(self,angle=0,axis=0): | ||||
|     def rotate(self, angle=0, axis=0): | ||||
|         c = math.cos(angle) | ||||
|         s = math.sin(angle) | ||||
|         t = 1 - c | ||||
| @@ -109,21 +106,22 @@ class Matrix3D: | ||||
|             self.n12 * other.n41 + self.n22 * other.n42 + self.n32 * other.n43 + self.n42 * other.n44, | ||||
|             self.n13 * other.n41 + self.n23 * other.n42 + self.n33 * other.n43 + self.n43 * other.n44, | ||||
|             self.n14 * other.n41 + self.n24 * other.n42 + self.n34 * other.n43 + self.n44 * other.n44 | ||||
|              ) | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class Point3D: | ||||
|     def __init__(self, x=0,y=0,z=0): | ||||
|     def __init__(self, x=0, y=0, z=0): | ||||
|         self.x = x | ||||
|         self.y = y | ||||
|         self.z = z | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[{0},{1},{2}]'.format(self.x, self.y,self.z) | ||||
|         return '[{0},{1},{2}]'.format(self.x, self.y, self.z) | ||||
|  | ||||
|     def string(self,prefix = "v"): | ||||
|         return '{0} {1:f} {2:f} {3:f}\n'.format(prefix ,self.x , self.y, self.z) | ||||
|     def string(self, prefix="v"): | ||||
|         return '{0} {1:f} {2:f} {3:f}\n'.format(prefix, self.x, self.y, self.z) | ||||
|  | ||||
|     def transformW(self,matrix): | ||||
|     def transformW(self, matrix): | ||||
|         x = matrix.n11 * self.x + matrix.n21 * self.y + matrix.n31 * self.z | ||||
|         y = matrix.n12 * self.x + matrix.n22 * self.y + matrix.n32 * self.z | ||||
|         z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z | ||||
| @@ -131,7 +129,7 @@ class Point3D: | ||||
|         self.y = y | ||||
|         self.z = z | ||||
|  | ||||
|     def transform(self,matrix): | ||||
|     def transform(self, matrix): | ||||
|         x = matrix.n11 * self.x + matrix.n21 * self.y + matrix.n31 * self.z + matrix.n41 | ||||
|         y = matrix.n12 * self.x + matrix.n22 * self.y + matrix.n32 * self.z + matrix.n42 | ||||
|         z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z + matrix.n43 | ||||
| @@ -140,41 +138,58 @@ class Point3D: | ||||
|         self.z = z | ||||
|  | ||||
|     def copy(self): | ||||
|         return Point3D(x=self.x,y=self.y,z=self.z) | ||||
|         return Point3D(x=self.x, y=self.y, z=self.z) | ||||
|  | ||||
|  | ||||
| class Point2D: | ||||
|     def __init__(self, x=0,y=0): | ||||
|     def __init__(self, x=0, y=0): | ||||
|         self.x = x | ||||
|         self.y = y | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[{0},{1}]'.format(self.x, self.y * -1) | ||||
|     def string(self,prefix="t"): | ||||
|         return '{0} {1:f} {2:f}\n'.format(prefix , self.x, self.y * -1 ) | ||||
|  | ||||
|     def string(self, prefix="t"): | ||||
|         return '{0} {1:f} {2:f}\n'.format(prefix, self.x, self.y * -1) | ||||
|  | ||||
|     def copy(self): | ||||
|         return Point2D(x=self.x,y=self.y) | ||||
|         return Point2D(x=self.x, y=self.y) | ||||
|  | ||||
|  | ||||
| class Face: | ||||
|     def __init__(self,a=0,b=0,c=0): | ||||
|     def __init__(self, a=0, b=0, c=0): | ||||
|         self.a = a | ||||
|         self.b = b | ||||
|         self.c = c | ||||
|     def string(self,prefix="f", indexOffset=0 ,textureoffset=0): | ||||
|  | ||||
|     def string(self, prefix="f", indexOffset=0, textureoffset=0): | ||||
|         if textureoffset == 0: | ||||
|             return prefix + ' {0}//{0} {1}//{1} {2}//{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset) | ||||
|         else: | ||||
|             return prefix + ' {0}/{3}/{0} {1}/{4}/{1} {2}/{5}/{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset,self.a + textureoffset, self.b + textureoffset, self.c + textureoffset) | ||||
|             return prefix + ' {0}/{3}/{0} {1}/{4}/{1} {2}/{5}/{2}\n'.format( | ||||
|                 self.a + indexOffset, | ||||
|                 self.b + indexOffset, | ||||
|                 self.c + indexOffset, | ||||
|                 self.a + textureoffset, | ||||
|                 self.b + textureoffset, | ||||
|                 self.c + textureoffset | ||||
|             ) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[{0},{1},{2}]'.format(self.a, self.b, self.c) | ||||
|  | ||||
|  | ||||
| class Group: | ||||
|     def __init__(self, node): | ||||
|         self.partRefs = node.getAttribute('partRefs').split(',') | ||||
|  | ||||
|  | ||||
| class Bone: | ||||
|     def __init__(self, node): | ||||
|         self.refID = node.getAttribute('refID') | ||||
|         (a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(',')) | ||||
|         self.matrix = Matrix3D(n11=a,n12=b,n13=c,n14=0,n21=d,n22=e,n23=f,n24=0,n31=g,n32=h,n33=i,n34=0,n41=x,n42=y,n43=z,n44=1) | ||||
|         self.matrix = Matrix3D(n11=a, n12=b, n13=c, n14=0, n21=d, n22=e, n23=f, n24=0, n31=g, n32=h, n33=i, n34=0, n41=x, n42=y, n43=z, n44=1) | ||||
|  | ||||
|  | ||||
| class Part: | ||||
|     def __init__(self, node): | ||||
| @@ -185,19 +200,17 @@ class Part: | ||||
|         self.designID = node.getAttribute('designID') | ||||
|         self.materials = list(map(str, node.getAttribute('materials').split(','))) | ||||
|  | ||||
|         lastm = '0' | ||||
|         for i, m in enumerate(self.materials): | ||||
|             if (m == '0'): | ||||
|                 # self.materials[i] = lastm | ||||
|                 self.materials[i] = self.materials[0] #in case of 0 choose the 'base' material | ||||
|             else: | ||||
|                 lastm = m | ||||
|                 self.materials[i] = self.materials[0]  # in case of 0 choose the 'base' material | ||||
|         if node.hasAttribute('decoration'): | ||||
|             self.decoration = list(map(str,node.getAttribute('decoration').split(','))) | ||||
|             self.decoration = list(map(str, node.getAttribute('decoration').split(','))) | ||||
|         for childnode in node.childNodes: | ||||
|             if childnode.nodeName == 'Bone': | ||||
|                 self.Bones.append(Bone(node=childnode)) | ||||
|  | ||||
|  | ||||
| class Brick: | ||||
|     def __init__(self, node): | ||||
|         self.refID = node.getAttribute('refID') | ||||
| @@ -207,14 +220,16 @@ class Brick: | ||||
|             if childnode.nodeName == 'Part': | ||||
|                 self.Parts.append(Part(node=childnode)) | ||||
|  | ||||
|  | ||||
| class SceneCamera: | ||||
|     def __init__(self, node): | ||||
|         self.refID = node.getAttribute('refID') | ||||
|         (a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(',')) | ||||
|         self.matrix = Matrix3D(n11=a,n12=b,n13=c,n14=0,n21=d,n22=e,n23=f,n24=0,n31=g,n32=h,n33=i,n34=0,n41=x,n42=y,n43=z,n44=1) | ||||
|         self.matrix = Matrix3D(n11=a, n12=b, n13=c, n14=0, n21=d, n22=e, n23=f, n24=0, n31=g, n32=h, n33=i, n34=0, n41=x, n42=y, n43=z, n44=1) | ||||
|         self.fieldOfView = float(node.getAttribute('fieldOfView')) | ||||
|         self.distance = float(node.getAttribute('distance')) | ||||
|  | ||||
|  | ||||
| class Scene: | ||||
|     def __init__(self, file): | ||||
|         self.Bricks = [] | ||||
| @@ -262,6 +277,7 @@ class Scene: | ||||
|  | ||||
|         # print('Scene "'+ self.Name + '" Brickversion: ' + str(self.Version)) | ||||
|  | ||||
|  | ||||
| class GeometryReader: | ||||
|     def __init__(self, data): | ||||
|         self.offset = 0 | ||||
| @@ -282,10 +298,10 @@ class GeometryReader: | ||||
|             options = self.readInt() | ||||
|  | ||||
|             for i in range(0, self.valueCount): | ||||
|                 self.positions.append(Point3D(x=self.readFloat(),y= self.readFloat(),z=self.readFloat())) | ||||
|                 self.positions.append(Point3D(x=self.readFloat(), y=self.readFloat(), z=self.readFloat())) | ||||
|  | ||||
|             for i in range(0, self.valueCount): | ||||
|                  self.normals.append(Point3D(x=self.readFloat(),y= self.readFloat(),z=self.readFloat())) | ||||
|                 self.normals.append(Point3D(x=self.readFloat(), y=self.readFloat(), z=self.readFloat())) | ||||
|  | ||||
|             if (options & 3) == 3: | ||||
|                 self.texCount = self.valueCount | ||||
| @@ -293,7 +309,7 @@ class GeometryReader: | ||||
|                     self.textures.append(Point2D(x=self.readFloat(), y=self.readFloat())) | ||||
|  | ||||
|             for i in range(0, self.faceCount): | ||||
|                 self.faces.append(Face(a=self.readInt(),b=self.readInt(),c=self.readInt())) | ||||
|                 self.faces.append(Face(a=self.readInt(), b=self.readInt(), c=self.readInt())) | ||||
|  | ||||
|             if (options & 48) == 48: | ||||
|                 num = self.readInt() | ||||
| @@ -311,7 +327,7 @@ class GeometryReader: | ||||
|                     boneoffset = self.readInt() + 4 | ||||
|                     self.bonemap[i] = self.read_Int(datastart + boneoffset) | ||||
|  | ||||
|     def read_Int(self,_offset): | ||||
|     def read_Int(self, _offset): | ||||
|         if sys.version_info < (3, 0): | ||||
|             return int(struct.unpack_from('i', self.data, _offset)[0]) | ||||
|         else: | ||||
| @@ -330,6 +346,7 @@ class GeometryReader: | ||||
|         self.offset += 4 | ||||
|         return ret | ||||
|  | ||||
|  | ||||
| class Geometry: | ||||
|     def __init__(self, designID, database): | ||||
|         self.designID = designID | ||||
| @@ -337,21 +354,25 @@ class Geometry: | ||||
|         self.maxGeoBounding = -1 | ||||
|         self.studsFields2D = [] | ||||
|  | ||||
|         GeometryLocation = '{0}{1}{2}'.format(GEOMETRIEPATH, designID,'.g') | ||||
|         GeometryLocation = '{0}{1}{2}'.format(GEOMETRIEPATH, designID, '.g') | ||||
|         GeometryCount = 0 | ||||
|         while str(GeometryLocation) in database.filelist: | ||||
|             self.Parts[GeometryCount] = GeometryReader(data=database.filelist[GeometryLocation].read()) | ||||
|             GeometryCount += 1 | ||||
|             GeometryLocation = '{0}{1}{2}{3}'.format(GEOMETRIEPATH, designID,'.g',GeometryCount) | ||||
|             GeometryLocation = '{0}{1}{2}{3}'.format(GEOMETRIEPATH, designID, '.g', GeometryCount) | ||||
|  | ||||
|         primitive = Primitive(data = database.filelist[PRIMITIVEPATH + designID + '.xml'].read()) | ||||
|         primitive = Primitive(data=database.filelist[PRIMITIVEPATH + designID + '.xml'].read()) | ||||
|         self.Partname = primitive.Designname | ||||
|         self.studsFields2D = primitive.Fields2D | ||||
|         try: | ||||
|             geoBoundingList = [abs(float(primitive.Bounding['minX']) - float(primitive.Bounding['maxX'])), abs(float(primitive.Bounding['minY']) - float(primitive.Bounding['maxY'])), abs(float(primitive.Bounding['minZ']) - float(primitive.Bounding['maxZ']))] | ||||
|             geoBoundingList = [ | ||||
|                 abs(float(primitive.Bounding['minX']) - float(primitive.Bounding['maxX'])), | ||||
|                 abs(float(primitive.Bounding['minY']) - float(primitive.Bounding['maxY'])), | ||||
|                 abs(float(primitive.Bounding['minZ']) - float(primitive.Bounding['maxZ'])) | ||||
|             ] | ||||
|             geoBoundingList.sort() | ||||
|             self.maxGeoBounding = geoBoundingList[-1] | ||||
|         except KeyError as e: | ||||
|         except KeyError: | ||||
|             # print('\nBounding errror in part {0}: {1}\n'.format(designID, e)) | ||||
|             pass | ||||
|  | ||||
| @@ -386,25 +407,33 @@ class Geometry: | ||||
|             count += self.Parts[part].texCount | ||||
|         return count | ||||
|  | ||||
|  | ||||
| class Bone2: | ||||
|     def __init__(self,boneId=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0): | ||||
|     def __init__(self, boneId=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0): | ||||
|         self.boneId = boneId | ||||
|         rotationMatrix = Matrix3D() | ||||
|         rotationMatrix.rotate(angle = -angle * math.pi / 180.0,axis = Point3D(x=ax,y=ay,z=az)) | ||||
|         p = Point3D(x=tx,y=ty,z=tz) | ||||
|         rotationMatrix.rotate( | ||||
|             angle=(-angle * math.pi / 180.0), | ||||
|             axis=Point3D(x=ax, y=ay, z=az) | ||||
|         ) | ||||
|         p = Point3D(x=tx, y=ty, z=tz) | ||||
|         p.transformW(rotationMatrix) | ||||
|         rotationMatrix.n41 -= p.x | ||||
|         rotationMatrix.n42 -= p.y | ||||
|         rotationMatrix.n43 -= p.z | ||||
|         self.matrix = rotationMatrix | ||||
|  | ||||
|  | ||||
| class Field2D: | ||||
|     def __init__(self, type=0, width=0, height=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0, field2DRawData='none'): | ||||
|         self.type = type | ||||
|         self.field2DRawData = field2DRawData | ||||
|         rotationMatrix = Matrix3D() | ||||
|         rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az)) | ||||
|         p = Point3D(x=tx,y=ty,z=tz) | ||||
|         rotationMatrix.rotate( | ||||
|             angle=(-angle * math.pi / 180.0), | ||||
|             axis=Point3D(x=ax, y=ay, z=az) | ||||
|         ) | ||||
|         p = Point3D(x=tx, y=ty, z=tz) | ||||
|         p.transformW(rotationMatrix) | ||||
|         rotationMatrix.n41 -= p.x | ||||
|         rotationMatrix.n42 -= p.y | ||||
| @@ -413,7 +442,7 @@ class Field2D: | ||||
|         self.matrix = rotationMatrix | ||||
|         self.custom2DField = [] | ||||
|  | ||||
|         #The height and width are always double the number of studs. The contained text is a 2D array that is always height + 1 and width + 1. | ||||
|         # The height and width are always double the number of studs. The contained text is a 2D array that is always height + 1 and width + 1. | ||||
|         rows_count = height + 1 | ||||
|         cols_count = width + 1 | ||||
|         # creation looks reverse | ||||
| @@ -432,18 +461,23 @@ class Field2D: | ||||
|     def __str__(self): | ||||
|         return '[type="{0}" transform="{1}" custom2DField="{2}"]'.format(self.type, self.matrix, self.custom2DField) | ||||
|  | ||||
|  | ||||
| class CollisionBox: | ||||
|     def __init__(self, sX=0, sY=0, sZ=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0): | ||||
|         rotationMatrix = Matrix3D() | ||||
|         rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az)) | ||||
|         p = Point3D(x=tx,y=ty,z=tz) | ||||
|         rotationMatrix.rotate( | ||||
|             angle=(-angle * math.pi / 180.0), | ||||
|             axis=Point3D(x=ax, y=ay, z=az) | ||||
|         ) | ||||
|         p = Point3D(x=tx,  y=ty, | ||||
|                     z=tz) | ||||
|         p.transformW(rotationMatrix) | ||||
|         rotationMatrix.n41 -= p.x | ||||
|         rotationMatrix.n42 -= p.y | ||||
|         rotationMatrix.n43 -= p.z | ||||
|  | ||||
|         self.matrix = rotationMatrix | ||||
|         self.corner = Point3D(x=sX,y=sY,z=sZ) | ||||
|         self.corner = Point3D(x=sX, y=sY, z=sZ) | ||||
|         self.positions = [] | ||||
|  | ||||
|         self.positions.append(Point3D(x=0, y=0, z=0)) | ||||
| @@ -452,11 +486,14 @@ class CollisionBox: | ||||
|         self.positions.append(Point3D(x=sX, y=sY, z=0)) | ||||
|         self.positions.append(Point3D(x=0, y=0, z=sZ)) | ||||
|         self.positions.append(Point3D(x=0, y=sY, z=sZ)) | ||||
|         self.positions.append(Point3D(x=sX ,y=0, z=sZ)) | ||||
|         self.positions.append(Point3D(x=sX ,y=sY, z=sZ)) | ||||
|         self.positions.append(Point3D(x=sX, y=0, z=sZ)) | ||||
|         self.positions.append(Point3D(x=sX, y=sY, z=sZ)) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '[0,0,0] [{0},0,0] [0,{1},0] [{0},{1},0] [0,0,{2}] [0,{1},{2}] [{0},0,{2}] [{0},{1},{2}]'.format(self.corner.x, self.corner.y, self.corner.z) | ||||
|         return '[0,0,0] [{0},0,0] [0,{1},0] [{0},{1},0] [0,0,{2}] [0,{1},{2}] [{0},0,{2}] [{0},{1},{2}]'.format( | ||||
|             self.corner.x, self.corner.y, self.corner.z | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class Primitive: | ||||
|     def __init__(self, data): | ||||
| @@ -475,7 +512,18 @@ class Primitive: | ||||
|             if node.nodeName == 'Flex': | ||||
|                 for node in node.childNodes: | ||||
|                     if node.nodeName == 'Bone': | ||||
|                         self.Bones.append(Bone2(boneId=int(node.getAttribute('boneId')), angle=float(node.getAttribute('angle')), ax=float(node.getAttribute('ax')), ay=float(node.getAttribute('ay')), az=float(node.getAttribute('az')), tx=float(node.getAttribute('tx')), ty=float(node.getAttribute('ty')), tz=float(node.getAttribute('tz')))) | ||||
|                         self.Bones.append( | ||||
|                             Bone2( | ||||
|                                 boneId=int(node.getAttribute('boneId')), | ||||
|                                 angle=float(node.getAttribute('angle')), | ||||
|                                 ax=float(node.getAttribute('ax')), | ||||
|                                 ay=float(node.getAttribute('ay')), | ||||
|                                 az=float(node.getAttribute('az')), | ||||
|                                 tx=float(node.getAttribute('tx')), | ||||
|                                 ty=float(node.getAttribute('ty')), | ||||
|                                 tz=float(node.getAttribute('tz')) | ||||
|                             ) | ||||
|                         ) | ||||
|             elif node.nodeName == 'Annotations': | ||||
|                 for childnode in node.childNodes: | ||||
|                     if childnode.nodeName == 'Annotation' and childnode.hasAttribute('designname'): | ||||
| @@ -483,23 +531,73 @@ class Primitive: | ||||
|             elif node.nodeName == 'Collision': | ||||
|                 for childnode in node.childNodes: | ||||
|                     if childnode.nodeName == 'Box': | ||||
|                         self.CollisionBoxes.append(CollisionBox(sX=float(childnode.getAttribute('sX')), sY=float(childnode.getAttribute('sY')), sZ=float(childnode.getAttribute('sZ')), angle=float(childnode.getAttribute('angle')), ax=float(childnode.getAttribute('ax')), ay=float(childnode.getAttribute('ay')), az=float(childnode.getAttribute('az')), tx=float(childnode.getAttribute('tx')), ty=float(childnode.getAttribute('ty')), tz=float(childnode.getAttribute('tz')))) | ||||
|                         self.CollisionBoxes.append( | ||||
|                             CollisionBox( | ||||
|                                 sX=float(childnode.getAttribute('sX')), | ||||
|                                 sY=float(childnode.getAttribute('sY')), | ||||
|                                 sZ=float(childnode.getAttribute('sZ')), | ||||
|                                 angle=float(childnode.getAttribute('angle')), | ||||
|                                 ax=float(childnode.getAttribute('ax')), | ||||
|                                 ay=float(childnode.getAttribute('ay')), | ||||
|                                 az=float(childnode.getAttribute('az')), | ||||
|                                 tx=float(childnode.getAttribute('tx')), | ||||
|                                 ty=float(childnode.getAttribute('ty')), | ||||
|                                 tz=float(childnode.getAttribute('tz')) | ||||
|                             ) | ||||
|                         ) | ||||
|             elif node.nodeName == 'PhysicsAttributes': | ||||
|                 self.PhysicsAttributes = {"inertiaTensor": node.getAttribute('inertiaTensor'),"centerOfMass": node.getAttribute('centerOfMass'),"mass": node.getAttribute('mass'),"frictionType": node.getAttribute('frictionType')} | ||||
|                 self.PhysicsAttributes = { | ||||
|                     "inertiaTensor": node.getAttribute('inertiaTensor'), | ||||
|                     "centerOfMass": node.getAttribute('centerOfMass'), | ||||
|                     "mass": node.getAttribute('mass'), | ||||
|                     "frictionType": node.getAttribute('frictionType') | ||||
|                 } | ||||
|             elif node.nodeName == 'Bounding': | ||||
|                 for childnode in node.childNodes: | ||||
|                     if childnode.nodeName == 'AABB': | ||||
|                         self.Bounding = {"minX": childnode.getAttribute('minX'), "minY": childnode.getAttribute('minY'), "minZ": childnode.getAttribute('minZ'), "maxX": childnode.getAttribute('maxX'), "maxY": childnode.getAttribute('maxY'), "maxZ": childnode.getAttribute('maxZ')} | ||||
|                         self.Bounding = { | ||||
|                             "minX": childnode.getAttribute('minX'), | ||||
|                             "minY": childnode.getAttribute('minY'), | ||||
|                             "minZ": childnode.getAttribute('minZ'), | ||||
|                             "maxX": childnode.getAttribute('maxX'), | ||||
|                             "maxY": childnode.getAttribute('maxY'), | ||||
|                             "maxZ": childnode.getAttribute('maxZ') | ||||
|                         } | ||||
|             elif node.nodeName == 'GeometryBounding': | ||||
|                 for childnode in node.childNodes: | ||||
|                     if childnode.nodeName == 'AABB': | ||||
|                         self.GeometryBounding = {"minX": childnode.getAttribute('minX'), "minY": childnode.getAttribute('minY'), "minZ": childnode.getAttribute('minZ'), "maxX": childnode.getAttribute('maxX'), "maxY": childnode.getAttribute('maxY'), "maxZ": childnode.getAttribute('maxZ')} | ||||
|                         self.GeometryBounding = { | ||||
|                             "minX": childnode.getAttribute('minX'), | ||||
|                             "minY": childnode.getAttribute('minY'), | ||||
|                             "minZ": childnode.getAttribute('minZ'), | ||||
|                             "maxX": childnode.getAttribute('maxX'), | ||||
|                             "maxY": childnode.getAttribute('maxY'), | ||||
|                             "maxZ": childnode.getAttribute('maxZ') | ||||
|                         } | ||||
|             elif node.nodeName == 'Connectivity': | ||||
|                 for childnode in node.childNodes: | ||||
|                     if childnode.nodeName == 'Custom2DField': | ||||
|                         self.Fields2D.append(Field2D(type=int(childnode.getAttribute('type')), width=int(childnode.getAttribute('width')), height=int(childnode.getAttribute('height')), angle=float(childnode.getAttribute('angle')), ax=float(childnode.getAttribute('ax')), ay=float(childnode.getAttribute('ay')), az=float(childnode.getAttribute('az')), tx=float(childnode.getAttribute('tx')), ty=float(childnode.getAttribute('ty')), tz=float(childnode.getAttribute('tz')), field2DRawData=str(childnode.firstChild.data))) | ||||
|                         self.Fields2D.append( | ||||
|                             Field2D( | ||||
|                                 type=int(childnode.getAttribute('type')), | ||||
|                                 width=int(childnode.getAttribute('width')), | ||||
|                                 height=int(childnode.getAttribute('height')), | ||||
|                                 angle=float(childnode.getAttribute('angle')), | ||||
|                                 ax=float(childnode.getAttribute('ax')), | ||||
|                                 ay=float(childnode.getAttribute('ay')), | ||||
|                                 az=float(childnode.getAttribute('az')), | ||||
|                                 tx=float(childnode.getAttribute('tx')), | ||||
|                                 ty=float(childnode.getAttribute('ty')), | ||||
|                                 tz=float(childnode.getAttribute('tz')), | ||||
|                                 field2DRawData=str(childnode.firstChild.data) | ||||
|                             ) | ||||
|                         ) | ||||
|             elif node.nodeName == 'Decoration': | ||||
|                 self.Decoration = {"faces": node.getAttribute('faces'), "subMaterialRedirectLookupTable": node.getAttribute('subMaterialRedirectLookupTable')} | ||||
|                 self.Decoration = { | ||||
|                     "faces": node.getAttribute('faces'), | ||||
|                     "subMaterialRedirectLookupTable": node.getAttribute('subMaterialRedirectLookupTable') | ||||
|                 } | ||||
|  | ||||
|  | ||||
| class Materials: | ||||
|     def __init__(self, data): | ||||
| @@ -514,13 +612,14 @@ class Materials: | ||||
|                     b=int(node.getAttribute('Blue')), | ||||
|                     a=int(node.getAttribute('Alpha')), | ||||
|                     mtype=str(node.getAttribute('MaterialType')) | ||||
|             ) | ||||
|                 ) | ||||
|  | ||||
|     def getMaterialbyId(self, mid): | ||||
|         return self.Materials[mid] | ||||
|  | ||||
|  | ||||
| class Material: | ||||
|     def __init__(self,id, r, g, b, a, mtype): | ||||
|     def __init__(self, id, r, g, b, a, mtype): | ||||
|         self.id = id | ||||
|         self.name = id | ||||
|         self.mattype = mtype | ||||
| @@ -528,18 +627,25 @@ class Material: | ||||
|         self.g = float(g) | ||||
|         self.b = float(b) | ||||
|         self.a = float(a) | ||||
|  | ||||
|     def string(self): | ||||
|         out = 'Kd {0} {1} {2}\nKa 1.600000 1.600000 1.600000\nKs 0.400000 0.400000 0.400000\nNs 3.482202\nTf 1 1 1\n'.format( self.r / 255, self.g / 255,self.b / 255) | ||||
|         out = 'Kd {0} {1} {2}\nKa 1.600000 1.600000 1.600000\nKs 0.400000 0.400000 0.400000\nNs 3.482202\nTf 1 1 1\n'.format( | ||||
|             self.r / 255, | ||||
|             self.g / 255, | ||||
|             self.b / 255 | ||||
|         ) | ||||
|         if self.a < 255: | ||||
|             out += 'Ni 1.575\n' + 'd {0}'.format(0.05) + '\n' + 'Tr {0}\n'.format(0.05) | ||||
|         return out | ||||
|  | ||||
|  | ||||
| class DBinfo: | ||||
|     def __init__(self, data): | ||||
|         xml = minidom.parseString(data) | ||||
|         self.Version = xml.getElementsByTagName('Bricks')[0].attributes['version'].value | ||||
|         # print('DB Version: ' + str(self.Version)) | ||||
|  | ||||
|  | ||||
| class DBFolderFile: | ||||
|     def __init__(self, name, handle): | ||||
|         self.handle = handle | ||||
| @@ -554,6 +660,7 @@ class DBFolderFile: | ||||
|         finally: | ||||
|             reader.close() | ||||
|  | ||||
|  | ||||
| class LIFFile: | ||||
|     def __init__(self, name, offset, size, handle): | ||||
|         self.handle = handle | ||||
| @@ -565,6 +672,7 @@ class LIFFile: | ||||
|         self.handle.seek(self.offset, 0) | ||||
|         return self.handle.read(self.size) | ||||
|  | ||||
|  | ||||
| class DBFolderReader: | ||||
|     def __init__(self, folder): | ||||
|         self.filelist = {} | ||||
| @@ -574,14 +682,14 @@ class DBFolderReader: | ||||
|  | ||||
|         try: | ||||
|             os.path.isdir(self.location) | ||||
|         except Exception as e: | ||||
|         except Exception: | ||||
|             self.initok = False | ||||
|             # print("db folder read FAIL") | ||||
|             return | ||||
|         else: | ||||
|             self.parse() | ||||
|             if self.fileexist(os.path.join(self.location,'Materials.xml')) and self.fileexist(os.path.join(self.location, 'info.xml')): | ||||
|                 self.dbinfo = DBinfo(data=self.filelist[os.path.join(self.location,'info.xml')].read()) | ||||
|             if self.fileexist(os.path.join(self.location, 'Materials.xml')) and self.fileexist(os.path.join(self.location, 'info.xml')): | ||||
|                 self.dbinfo = DBinfo(data=self.filelist[os.path.join(self.location, 'info.xml')].read()) | ||||
|                 # print("DB folder OK.") | ||||
|                 self.initok = True | ||||
|             else: | ||||
| @@ -593,7 +701,6 @@ class DBFolderReader: | ||||
|                 # print(MATERIALNAMESPATH) | ||||
|                 pass | ||||
|  | ||||
|  | ||||
|     def fileexist(self, filename): | ||||
|         return filename in self.filelist | ||||
|  | ||||
| @@ -603,6 +710,7 @@ class DBFolderReader: | ||||
|                 entryName = os.path.join(path, name) | ||||
|                 self.filelist[entryName] = DBFolderFile(name=entryName, handle=entryName) | ||||
|  | ||||
|  | ||||
| class LIFReader: | ||||
|     def __init__(self, file): | ||||
|         self.packedFilesOffset = 84 | ||||
| @@ -614,7 +722,7 @@ class LIFReader: | ||||
|         try: | ||||
|             self.filehandle = open(self.location, "rb") | ||||
|             self.filehandle.seek(0, 0) | ||||
|         except Exception as e: | ||||
|         except Exception: | ||||
|             self.initok = False | ||||
|             # print("Database FAIL") | ||||
|             return | ||||
| @@ -632,7 +740,7 @@ class LIFReader: | ||||
|                 # print("Database FAIL") | ||||
|                 self.initok = False | ||||
|  | ||||
|     def fileexist(self,filename): | ||||
|     def fileexist(self, filename): | ||||
|         return filename in self.filelist | ||||
|  | ||||
|     def parse(self, prefix='', offset=0): | ||||
| @@ -648,7 +756,7 @@ class LIFReader: | ||||
|             entryType = self.readShort(offset=offset) | ||||
|             offset += 6 | ||||
|  | ||||
|             entryName = '{0}{1}'.format(prefix,'/'); | ||||
|             entryName = '{0}{1}'.format(prefix, '/') | ||||
|             self.filehandle.seek(offset + 1, 0) | ||||
|             if sys.version_info < (3, 0): | ||||
|                 t = ord(self.filehandle.read(1)) | ||||
| @@ -656,7 +764,7 @@ class LIFReader: | ||||
|                 t = int.from_bytes(self.filehandle.read(1), byteorder='big') | ||||
|  | ||||
|             while not t == 0: | ||||
|                 entryName ='{0}{1}'.format(entryName,chr(t)) | ||||
|                 entryName = '{0}{1}'.format(entryName, chr(t)) | ||||
|                 self.filehandle.seek(1, 1) | ||||
|                 if sys.version_info < (3, 0): | ||||
|                     t = ord(self.filehandle.read(1)) | ||||
| @@ -692,33 +800,32 @@ class LIFReader: | ||||
|         else: | ||||
|             return int.from_bytes(self.filehandle.read(2), byteorder='big') | ||||
|  | ||||
|  | ||||
| class Converter: | ||||
|     def LoadDBFolder(self, dbfolderlocation): | ||||
|         self.database = DBFolderReader(folder=dbfolderlocation) | ||||
|         if self.database.initok and self.database.fileexist(os.path.join(dbfolderlocation,'Materials.xml')): | ||||
|             self.allMaterials = Materials(data=self.database.filelist[os.path.join(dbfolderlocation,'Materials.xml')].read()); | ||||
|         if self.database.initok and self.database.fileexist(os.path.join(dbfolderlocation, 'Materials.xml')): | ||||
|             self.allMaterials = Materials(data=self.database.filelist[os.path.join(dbfolderlocation, 'Materials.xml')].read()) | ||||
|  | ||||
|     def LoadDatabase(self,databaselocation): | ||||
|     def LoadDatabase(self, databaselocation): | ||||
|         self.database = LIFReader(file=databaselocation) | ||||
|  | ||||
|         if self.database.initok and self.database.fileexist('/Materials.xml'): | ||||
|             self.allMaterials = Materials(data=self.database.filelist['/Materials.xml'].read()); | ||||
|             self.allMaterials = Materials(data=self.database.filelist['/Materials.xml'].read()) | ||||
|  | ||||
|     def LoadScene(self,filename): | ||||
|     def LoadScene(self, filename): | ||||
|         if self.database.initok: | ||||
|             self.scene = Scene(file=filename) | ||||
|  | ||||
|     def Export(self,filename): | ||||
|     def Export(self, filename): | ||||
|         invert = Matrix3D() | ||||
|         #invert.n33 = -1 #uncomment to invert the Z-Axis | ||||
|         # invert.n33 = -1 #uncomment to invert the Z-Axis | ||||
|  | ||||
|         indexOffset = 1 | ||||
|         textOffset = 1 | ||||
|         usedmaterials = [] | ||||
|         geometriecache = {} | ||||
|  | ||||
|         start_time = time.time() | ||||
|  | ||||
|         out = open(filename + ".obj.tmp", "w+") | ||||
|         out.truncate(0) | ||||
|         out.write("mtllib " + filename + ".mtl" + '\n\n') | ||||
| @@ -735,12 +842,12 @@ class Converter: | ||||
|  | ||||
|                 if pa.designID not in geometriecache: | ||||
|                     geo = Geometry(designID=pa.designID, database=self.database) | ||||
|                     progress(current ,total , "(" + geo.designID + ") " + geo.Partname, ' ') | ||||
|                     progress(current, total, "(" + geo.designID + ") " + geo.Partname, ' ') | ||||
|                     geometriecache[pa.designID] = geo | ||||
|                 else: | ||||
|                     geo = geometriecache[pa.designID] | ||||
|  | ||||
|                     progress(current ,total , "(" + geo.designID + ") " + geo.Partname ,'-') | ||||
|                     progress(current, total, "(" + geo.designID + ") " + geo.Partname, '-') | ||||
|  | ||||
|                 out.write("o\n") | ||||
|  | ||||
| @@ -752,11 +859,11 @@ class Converter: | ||||
|                         # positions | ||||
|                         for j, p in enumerate(geo.Parts[part].outpositions): | ||||
|                             if (geo.Parts[part].bonemap[j] == i): | ||||
|                                 p.transform( invert * b.matrix) | ||||
|                                 p.transform(invert * b.matrix) | ||||
|                         # normals | ||||
|                         for k, n in enumerate(geo.Parts[part].outnormals): | ||||
|                             if (geo.Parts[part].bonemap[k] == i): | ||||
|                                 n.transformW( invert * b.matrix) | ||||
|                                 n.transformW(invert * b.matrix) | ||||
|  | ||||
|                     for point in geo.Parts[part].outpositions: | ||||
|                         out.write(point.string("v")) | ||||
| @@ -772,13 +879,12 @@ class Converter: | ||||
|                 last_color = 0 | ||||
|                 for part in geo.Parts: | ||||
|  | ||||
|  | ||||
|                     #try catch here for possible problems in materials assignment of various g, g1, g2, .. files in lxf file | ||||
|                     # try catch here for possible problems in materials assignment of various g, g1, g2, .. files in lxf file | ||||
|                     try: | ||||
|                         materialCurrentPart = pa.materials[part] | ||||
|                         last_color = pa.materials[part] | ||||
|                     except IndexError: | ||||
|                         # print('WARNING: {0}.g{1} has NO material assignment in lxf. Replaced with color {2}. Fix {0}.xml faces values.'.format(pa.designID, part, last_color)) | ||||
|  | ||||
|                         materialCurrentPart = last_color | ||||
|  | ||||
|                     lddmat = self.allMaterials.getMaterialbyId(materialCurrentPart) | ||||
| @@ -786,7 +892,7 @@ class Converter: | ||||
|  | ||||
|                     deco = '0' | ||||
|                     if hasattr(pa, 'decoration') and len(geo.Parts[part].textures) > 0: | ||||
|                         #if decoCount <= len(pa.decoration): | ||||
|                         # if decoCount <= len(pa.decoration): | ||||
|                         if decoCount < len(pa.decoration): | ||||
|                             deco = pa.decoration[decoCount] | ||||
|                         decoCount += 1 | ||||
| @@ -801,7 +907,7 @@ class Converter: | ||||
|                                 f.write(self.database.filelist[decofilename].read()) | ||||
|                                 f.close() | ||||
|  | ||||
|                     if not matname in usedmaterials: | ||||
|                     if matname not in usedmaterials: | ||||
|                         usedmaterials.append(matname) | ||||
|                         outtext.write("newmtl " + matname + '\n') | ||||
|                         outtext.write(lddmat.string()) | ||||
| @@ -811,9 +917,9 @@ class Converter: | ||||
|                     out.write("usemtl " + matname + '\n') | ||||
|                     for face in geo.Parts[part].faces: | ||||
|                         if len(geo.Parts[part].textures) > 0: | ||||
|                             out.write(face.string("f",indexOffset,textOffset)) | ||||
|                             out.write(face.string("f", indexOffset, textOffset)) | ||||
|                         else: | ||||
|                             out.write(face.string("f",indexOffset)) | ||||
|                             out.write(face.string("f", indexOffset)) | ||||
|  | ||||
|                     indexOffset += len(geo.Parts[part].outpositions) | ||||
|                     textOffset += len(geo.Parts[part].textures) | ||||
| @@ -825,6 +931,7 @@ class Converter: | ||||
|         sys.stdout.write('%s\r' % ('                                                                                                 ')) | ||||
|         # print("--- %s seconds ---" % (time.time() - start_time)) | ||||
|  | ||||
|  | ||||
| def setDBFolderVars(dbfolderlocation, lod): | ||||
|     global PRIMITIVEPATH | ||||
|     global GEOMETRIEPATH | ||||
| @@ -836,44 +943,8 @@ def setDBFolderVars(dbfolderlocation, lod): | ||||
|     MATERIALNAMESPATH = os.path.join(dbfolderlocation, 'MaterialNames', '') | ||||
|     # print(MATERIALNAMESPATH) | ||||
|  | ||||
| def FindDatabase(): | ||||
|     lddliftree = os.getenv('LDDLIFTREE') | ||||
|     if lddliftree is not None: | ||||
|         if os.path.isdir(str(lddliftree)): #LDDLIFTREE points to folder | ||||
|             return str(lddliftree) | ||||
|         elif os.path.isfile(str(lddliftree)): #LDDLIFTREE points to file (should be db.lif) | ||||
|             return str(lddliftree) | ||||
|  | ||||
|     else: #Env variable LDDLIFTREE not set. Check for default locations per different platform. | ||||
|         if platform.system() == 'Darwin': | ||||
|             if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db')) | ||||
|             elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db.lif'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db.lif')) | ||||
|             else: | ||||
|                 # print("no LDD database found please install LEGO-Digital-Designer") | ||||
|                 os._exit() | ||||
|         elif platform.system() == 'Windows': | ||||
|             if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db')) | ||||
|             elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db.lif'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db.lif')) | ||||
|             else: | ||||
|                 # print("no LDD database found please install LEGO-Digital-Designer") | ||||
|                 os._exit() | ||||
|         elif platform.system() == 'Linux': | ||||
|             if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db')) | ||||
|             elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db.lif'))): | ||||
|                 return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db.lif')) | ||||
|             else: | ||||
|                 # print("no LDD database found please install LEGO-Digital-Designer") | ||||
|                 os._exit() | ||||
|         else: | ||||
|             # print('Your OS {0} is not supported yet.'.format(platform.system())) | ||||
|             os._exit() | ||||
|  | ||||
| def progress(count, total, status='', suffix = ''): | ||||
| def progress(count, total, status='', suffix=''): | ||||
|     bar_len = 40 | ||||
|     filled_len = int(round(bar_len * count / float(total))) | ||||
|     percents = round(100.0 * count / float(total), 1) | ||||
| @@ -882,6 +953,7 @@ def progress(count, total, status='', suffix = ''): | ||||
|     sys.stdout.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, status)) | ||||
|     sys.stdout.flush() | ||||
|  | ||||
|  | ||||
| def main(lxf_filename, obj_filename, lod="2"): | ||||
|     # print("- - - pylddlib - - -") | ||||
|     # print("          _ ") | ||||
| @@ -896,10 +968,11 @@ def main(lxf_filename, obj_filename, lod="2"): | ||||
|     GEOMETRIEPATH = GEOMETRIEPATH + f"LOD{lod}/" | ||||
|     converter = Converter() | ||||
|     # print("Found DB folder. Will use this instead of db.lif!") | ||||
|     setDBFolderVars(dbfolderlocation = "app/luclient/res/", lod=lod) | ||||
|     converter.LoadDBFolder(dbfolderlocation = "app/luclient/res/") | ||||
|     setDBFolderVars(dbfolderlocation="app/luclient/res/", lod=lod) | ||||
|     converter.LoadDBFolder(dbfolderlocation="app/luclient/res/") | ||||
|     converter.LoadScene(filename=lxf_filename) | ||||
|     converter.Export(filename=obj_filename) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, request, current_app | ||||
| from flask_user import login_required, current_user | ||||
| from app.models import db, CharacterInfo, Account, CharacterXML, Reports | ||||
| from flask import render_template, Blueprint, current_app | ||||
| from flask_user import login_required | ||||
| from app.models import CharacterInfo, Account, CharacterXML, Reports | ||||
| from app import gm_level, scheduler | ||||
| import datetime, xmltodict, json | ||||
| import datetime | ||||
| import xmltodict | ||||
|  | ||||
| reports_blueprint = Blueprint('reports', __name__) | ||||
|  | ||||
|  | ||||
| @reports_blueprint.route('/', methods=['GET', 'POST']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| @@ -13,25 +15,28 @@ def index(): | ||||
|     reports = Reports.query.distinct(Reports.date).group_by(Reports.date).all() | ||||
|     return render_template('reports/index.html.j2', reports=reports) | ||||
|  | ||||
|  | ||||
| @reports_blueprint.route('/items/by_date/<date>', methods=['GET', 'POST']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| def items_by_date(date): | ||||
|     data = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="items").first().data | ||||
|     data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data | ||||
|     return render_template('reports/items/by_date.html.j2', data=data, date=date) | ||||
|  | ||||
|  | ||||
| @reports_blueprint.route('/currency/by_date/<date>', methods=['GET', 'POST']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| def currency_by_date(date): | ||||
|     data = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="currency").first().data | ||||
|     data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data | ||||
|     return render_template('reports/currency/by_date.html.j2', data=data, date=date) | ||||
|  | ||||
|  | ||||
| @reports_blueprint.route('/uscore/by_date/<date>', methods=['GET', 'POST']) | ||||
| @login_required | ||||
| @gm_level(3) | ||||
| def uscore_by_date(date): | ||||
|     data = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="uscore").first().data | ||||
|     data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first().data | ||||
|     return render_template('reports/uscore/by_date.html.j2', data=data, date=date) | ||||
|  | ||||
|  | ||||
| @@ -42,22 +47,22 @@ def gen_item_report(): | ||||
|             current_app.logger.info("Start Item Report Generation") | ||||
|  | ||||
|             date = datetime.date.today().strftime('%Y-%m-%d') | ||||
|             report = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="items").first() | ||||
|             report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first() | ||||
|  | ||||
|             # Only one report per day | ||||
|             if report != None: | ||||
|             if report not None: | ||||
|                 current_app.logger.info(f"Item Report Already Generated for {date}") | ||||
|                 return | ||||
|  | ||||
|             char_xmls = CharacterXML.query.join( | ||||
|                             CharacterInfo, | ||||
|                             CharacterInfo.id==CharacterXML.id | ||||
|                         ).join( | ||||
|                             Account, | ||||
|                             CharacterInfo.account_id==Account.id | ||||
|                         ).filter(Account.gm_level < 3).all() | ||||
|                 CharacterInfo, | ||||
|                 CharacterInfo.id == CharacterXML.id | ||||
|             ).join( | ||||
|                 Account, | ||||
|                 CharacterInfo.account_id == Account.id | ||||
|             ).filter(Account.gm_level < 3).all() | ||||
|  | ||||
|             report_data={} | ||||
|             report_data = {} | ||||
|  | ||||
|             for char_xml in char_xmls: | ||||
|                 try: | ||||
| @@ -66,7 +71,7 @@ def gen_item_report(): | ||||
|                         attr_prefix="attr_" | ||||
|                     ) | ||||
|                     for inv in character_json["obj"]["inv"]["items"]["in"]: | ||||
|                         if "i" in inv.keys() and type(inv["i"]) == list and (int(inv["attr_t"])==0 or int(inv["attr_t"])==1): | ||||
|                         if "i" in inv.keys() and type(inv["i"]) == list and (int(inv["attr_t"]) == 0 or int(inv["attr_t"]) == 1): | ||||
|                             for item in inv["i"]: | ||||
|                                 if item["attr_l"] in report_data: | ||||
|                                     report_data[item["attr_l"]] = report_data[item["attr_l"]] + int(item["attr_c"]) | ||||
| @@ -96,22 +101,22 @@ def gen_currency_report(): | ||||
|             current_app.logger.info("Start Currency Report Generation") | ||||
|  | ||||
|             date = datetime.date.today().strftime('%Y-%m-%d') | ||||
|             report = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="currency").first() | ||||
|             report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first() | ||||
|  | ||||
|             # Only one report per day | ||||
|             if report != None: | ||||
|             if report not None: | ||||
|                 current_app.logger.info(f"Currency Report Already Generated for {date}") | ||||
|                 return | ||||
|  | ||||
|             characters = CharacterXML.query.join( | ||||
|                             CharacterInfo, | ||||
|                             CharacterInfo.id==CharacterXML.id | ||||
|                         ).join( | ||||
|                             Account, | ||||
|                             CharacterInfo.account_id==Account.id | ||||
|                         ).filter(Account.gm_level < 3).all() | ||||
|                 CharacterInfo, | ||||
|                 CharacterInfo.id == CharacterXML.id | ||||
|             ).join( | ||||
|                 Account, | ||||
|                 CharacterInfo.account_id == Account.id | ||||
|             ).filter(Account.gm_level < 3).all() | ||||
|  | ||||
|             report_data={} | ||||
|             report_data = {} | ||||
|  | ||||
|             for character in characters: | ||||
|                 try: | ||||
| @@ -119,7 +124,7 @@ def gen_currency_report(): | ||||
|                         character.xml_data, | ||||
|                         attr_prefix="attr_" | ||||
|                     ) | ||||
|                     report_data[CharacterInfo.query.filter(CharacterInfo.id==character.id).first().name] = int(character_json["obj"]["char"]["attr_cc"]) | ||||
|                     report_data[CharacterInfo.query.filter(CharacterInfo.id == character.id).first().name] = int(character_json["obj"]["char"]["attr_cc"]) | ||||
|                 except Exception as e: | ||||
|                     current_app.logger.error(f"REPORT::CURRENCY - ERROR PARSING CHARACTER {char_xml.id}") | ||||
|                     current_app.logger.error(f"REPORT::CURRENCY - {e}") | ||||
| @@ -144,22 +149,22 @@ def gen_uscore_report(): | ||||
|             current_app.logger.info("Start U-Score Report Generation") | ||||
|  | ||||
|             date = datetime.date.today().strftime('%Y-%m-%d') | ||||
|             report = Reports.query.filter(Reports.date==date).filter(Reports.report_type=="uscore").first() | ||||
|             report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first() | ||||
|  | ||||
|             # Only one report per day | ||||
|             if report != None: | ||||
|             if report not None: | ||||
|                 current_app.logger.info(f"U-Score Report Already Generated for {date}") | ||||
|                 return | ||||
|  | ||||
|             characters = CharacterXML.query.join( | ||||
|                             CharacterInfo, | ||||
|                             CharacterInfo.id==CharacterXML.id | ||||
|                         ).join( | ||||
|                             Account, | ||||
|                             CharacterInfo.account_id==Account.id | ||||
|                         ).filter(Account.gm_level < 3).all() | ||||
|                 CharacterInfo, | ||||
|                 CharacterInfo.id == CharacterXML.id | ||||
|             ).join( | ||||
|                 Account, | ||||
|                 CharacterInfo.account_id == Account.id | ||||
|             ).filter(Account.gm_level < 3).all() | ||||
|  | ||||
|             report_data={} | ||||
|             report_data = {} | ||||
|  | ||||
|             for character in characters: | ||||
|                 try: | ||||
| @@ -167,7 +172,7 @@ def gen_uscore_report(): | ||||
|                         character.xml_data, | ||||
|                         attr_prefix="attr_" | ||||
|                     ) | ||||
|                     report_data[CharacterInfo.query.filter(CharacterInfo.id==character.id).first().name] = int(character_json["obj"]["char"]["attr_ls"]) | ||||
|                     report_data[CharacterInfo.query.filter(CharacterInfo.id == character.id).first().name] = int(character_json["obj"]["char"]["attr_ls"]) | ||||
|                 except Exception as e: | ||||
|                     current_app.logger.error(f"REPORT::U-SCORE - ERROR PARSING CHARACTER {char_xml.id}") | ||||
|                     current_app.logger.error(f"REPORT::U-SCORE - {e}") | ||||
|   | ||||
| @@ -1,7 +1,21 @@ | ||||
| from flask_marshmallow import Marshmallow | ||||
| from app.models import * | ||||
| from app.models import ( | ||||
|     PlayKey | ||||
|     PetNames | ||||
|     Mail | ||||
|     UGC | ||||
|     PropertyContent | ||||
|     Property | ||||
|     CharacterXML | ||||
|     CharacterInfo | ||||
|     Account | ||||
|     AccountInvitation | ||||
|     ActivityLog | ||||
|     CommandLog | ||||
| ) | ||||
| ma = Marshmallow() | ||||
|  | ||||
|  | ||||
| class PlayKeySchema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = PlayKey | ||||
| @@ -44,7 +58,6 @@ class PropertyContentSchema(ma.SQLAlchemyAutoSchema): | ||||
|     ugc = ma.Nested(UGCSchema) | ||||
|  | ||||
|  | ||||
|  | ||||
| class PropertySchema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = Property | ||||
|   | ||||
							
								
								
									
										6
									
								
								pylama.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pylama.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| [pylama] | ||||
| ignore = D203, D212, D213, D406, D407, D408, D409, D100, D104, D401, F722 | ||||
| max_line_length = 160 | ||||
|  | ||||
| [pylama:mccabe] | ||||
| max-complexity = 35 | ||||
		Reference in New Issue
	
	Block a user
	 Aaron Kimbre
					Aaron Kimbre