syntax/linting fixes

This commit is contained in:
Aaron Kimbre 2022-03-12 20:09:35 -06:00
parent 70742549b9
commit 5ce9ac85bc
18 changed files with 504 additions and 375 deletions

View File

@ -10,7 +10,7 @@ from app.forms import CustomUserManager
from flask_user import user_registered, current_user, user_logged_in from flask_user import user_registered, current_user, user_logged_in
from flask_wtf.csrf import CSRFProtect from flask_wtf.csrf import CSRFProtect
from flask_apscheduler import APScheduler 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.commands import init_db, init_accounts, load_property, gen_image_cache, gen_model_cache
from app.models import Account, AccountInvitation, AuditLog from app.models import Account, AccountInvitation, AuditLog
@ -40,13 +40,12 @@ def create_app():
app.logger.info( app.logger.info(
f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered \ 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}" using Play Key ID {play_key_used.id} : {play_key_used.key_string}"
) )
db.session.add(play_key_used) db.session.add(play_key_used)
db.session.commit() db.session.commit()
else: else:
app.logger.info(f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered") app.logger.info(f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered")
@user_logged_in.connect_via(app) @user_logged_in.connect_via(app)
def _after_login_hook(sender, user, **extra): def _after_login_hook(sender, user, **extra):
app.logger.info(f"{user.username} Logged in") app.logger.info(f"{user.username} Logged in")
@ -55,7 +54,7 @@ def create_app():
@app.template_filter('ctime') @app.template_filter('ctime')
def timectime(s): def timectime(s):
if s: if s:
return time.ctime(s) # or datetime.datetime.fromtimestamp(s) return time.ctime(s) # or datetime.datetime.fromtimestamp(s)
else: else:
return "Never" return "Never"
@ -246,6 +245,7 @@ def gm_level(gm_level):
return wrapper return wrapper
return decorator return decorator
def log_audit(message): def log_audit(message):
AuditLog( AuditLog(
account_id=current_user.id, account_id=current_user.id,

View File

@ -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 from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
import datetime import datetime
import time from app.models import Account, db
from app.models import Account, AccountInvitation, db
from app.schemas import AccountSchema from app.schemas import AccountSchema
from app import gm_level, log_audit from app import gm_level, log_audit
from app.forms import EditGMLevelForm from app.forms import EditGMLevelForm
@ -13,6 +11,7 @@ accounts_blueprint = Blueprint('accounts', __name__)
account_schema = AccountSchema() account_schema = AccountSchema()
@accounts_blueprint.route('/', methods=['GET']) @accounts_blueprint.route('/', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -38,7 +37,7 @@ def edit_gm_level(id):
if current_user.id == int(id): if current_user.id == int(id):
flash("You cannot your own GM Level", "danger") flash("You cannot your own GM Level", "danger")
return redirect(request.referrer if request.referrer else url_for("main.index")) 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: if account_data.gm_level >= 8 and current_user.gm_level == 8:
flash("You cannot edit this user's GM Level", "warning") flash("You cannot edit this user's GM Level", "warning")
return redirect(request.referrer if request.referrer else url_for("main.index")) return redirect(request.referrer if request.referrer else url_for("main.index"))
@ -138,10 +137,10 @@ def get():
View View
</a> </a>
""" """
# <a role="button" class="btn btn-danger btn btn-block" # <a role="button" class="btn btn-danger btn btn-block"
# href='{url_for('acounts.delete', id=account["0"])}'> # href='{url_for('acounts.delete', id=account["0"])}'>
# Delete # Delete
# </a> # </a>
if account["4"]: if account["4"]:
account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' 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>''' account["5"] = '''<h2 class="far fa-check-square text-success"></h2>'''
if account["6"]: 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: else:
account["6"] = '''<h2 class="far fa-check-square text-success"></h2>''' account["6"] = '''<h2 class="far fa-check-square text-success"></h2>'''
if current_app.config["USER_ENABLE_EMAIL"]: if current_app.config["USER_ENABLE_EMAIL"]:
if account["8"]: 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: else:
account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
else: else:
@ -175,4 +174,3 @@ def get():
del account["8"] del account["8"]
return data return data

View File

@ -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 flask_user import login_required, current_user
from app.models import db, BugReport, CharacterInfo from app.models import db, BugReport, CharacterInfo
from datatables import ColumnDT, DataTables 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 = Blueprint('bug_reports', __name__)
@bug_report_blueprint.route('/<status>', methods=['GET']) @bug_report_blueprint.route('/<status>', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -23,7 +24,7 @@ def view(id):
if report.resoleved_by: if report.resoleved_by:
rb = report.resoleved_by.username rb = report.resoleved_by.username
else: else:
rb="" rb = ""
return render_template('bug_reports/view.html.j2', report=report, resolved_by=rb) return render_template('bug_reports/view.html.j2', report=report, resolved_by=rb)
@ -62,12 +63,12 @@ def get(status):
] ]
query = None query = None
if status=="all": if status == "all":
query = db.session.query().select_from(BugReport) query = db.session.query().select_from(BugReport)
elif status=="resolved": elif status == "resolved":
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time != None) query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time not None)
elif status=="unresolved": elif status == "unresolved":
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time == None) query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time is None)
else: else:
raise Exception("Not a valid filter") raise Exception("Not a valid filter")

View File

@ -1,8 +1,7 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response
from flask_user import login_required, current_user from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
import datetime, time import time
from app.models import CharacterInfo, CharacterXML, Account, db from app.models import CharacterInfo, CharacterXML, Account, db
from app.schemas import CharacterInfoSchema from app.schemas import CharacterInfoSchema
from app.forms import RescueForm from app.forms import RescueForm
@ -16,6 +15,7 @@ character_blueprint = Blueprint('characters', __name__)
character_schema = CharacterInfoSchema() character_schema = CharacterInfoSchema()
@character_blueprint.route('/', methods=['GET']) @character_blueprint.route('/', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -27,7 +27,7 @@ def index():
@login_required @login_required
@gm_level(3) @gm_level(3)
def approve_name(id, action): def approve_name(id, action):
character = CharacterInfo.query.filter(CharacterInfo.id == id).first() character = CharacterInfo.query.filter(CharacterInfo.id == id).first()
if action == "approve": if action == "approve":
log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}") log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}")
@ -42,9 +42,12 @@ def approve_name(id, action):
elif action == "rename": elif action == "rename":
character.needs_rename = True 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( 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" "danger"
) )
@ -67,11 +70,11 @@ def view(id):
abort(403) abort(403)
return return
character_json = xmltodict.parse( character_json = xmltodict.parse(
CharacterXML.query.filter( CharacterXML.query.filter(
CharacterXML.id==id CharacterXML.id == id
).first().xml_data, ).first().xml_data,
attr_prefix="attr_" attr_prefix="attr_"
) )
# print json for reference # print json for reference
# with open("errorchar.json", "a") as file: # with open("errorchar.json", "a") as file:
@ -84,8 +87,7 @@ def view(id):
# sort by items slot index # sort by items slot index
for inv in character_json["obj"]["inv"]["holdings"]["in"]: for inv in character_json["obj"]["inv"]["holdings"]["in"]:
if "i" in inv.keys() and type(inv["i"]) == list: 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( return render_template(
'character/view.html.j2', 'character/view.html.j2',
@ -110,13 +112,14 @@ def view_xml(id):
return return
character_xml = CharacterXML.query.filter( character_xml = CharacterXML.query.filter(
CharacterXML.id==id CharacterXML.id == id
).first().xml_data ).first().xml_data
response = make_response(character_xml) response = make_response(character_xml)
response.headers.set('Content-Type', 'text/xml') response.headers.set('Content-Type', 'text/xml')
return response return response
@character_blueprint.route('/get_xml/<id>', methods=['GET']) @character_blueprint.route('/get_xml/<id>', methods=['GET'])
@login_required @login_required
def get_xml(id): def get_xml(id):
@ -132,8 +135,8 @@ def get_xml(id):
return return
character_xml = CharacterXML.query.filter( character_xml = CharacterXML.query.filter(
CharacterXML.id==id CharacterXML.id == id
).first().xml_data ).first().xml_data
response = make_response(character_xml) response = make_response(character_xml)
response.headers.set('Content-Type', 'attachment/xml') response.headers.set('Content-Type', 'attachment/xml')
@ -144,6 +147,7 @@ def get_xml(id):
) )
return response return response
@character_blueprint.route('/restrict/<bit>/<id>', methods=['GET']) @character_blueprint.route('/restrict/<bit>/<id>', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -177,8 +181,8 @@ def rescue(id):
form = RescueForm() form = RescueForm()
character_data = CharacterXML.query.filter( character_data = CharacterXML.query.filter(
CharacterXML.id==id CharacterXML.id == id
).first() ).first()
character_xml = ET.XML(character_data.xml_data) character_xml = ET.XML(character_data.xml_data)
for zone in character_xml.findall('.//r'): for zone in character_xml.findall('.//r'):
@ -205,6 +209,7 @@ def rescue(id):
return render_template("character/rescue.html.j2", form=form) return render_template("character/rescue.html.j2", form=form)
@character_blueprint.route('/get/<status>', methods=['GET']) @character_blueprint.route('/get/<status>', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -220,12 +225,12 @@ def get(status):
] ]
query = None query = None
if status=="all": if status == "all":
query = db.session.query().select_from(CharacterInfo).join(Account) query = db.session.query().select_from(CharacterInfo).join(Account)
elif status=="approved": elif status == "approved":
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename == False)) query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename is False))
elif status=="unapproved": elif status == "unapproved":
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename == True)) query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename is True))
else: else:
raise Exception("Not a valid filter") raise Exception("Not a valid filter")
@ -286,6 +291,4 @@ def get(status):
if perm_map & (1 << 6): if perm_map & (1 << 6):
character["6"] += "Restricted Chat</br>" character["6"] += "Restricted Chat</br>"
return data return data

View File

@ -1,19 +1,20 @@
import click import click
import json
from flask.cli import with_appcontext from flask.cli import with_appcontext
import random, string, datetime import random
import string
import datetime
from flask_user import current_app from flask_user import current_app
from app import db from app import db
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC
import pathlib import pathlib
import zlib import zlib
import os
from wand import image from wand import image
from wand.exceptions import BlobError as BE from wand.exceptions import BlobError as BE
import app.pylddlib as ldd import app.pylddlib as ldd
from multiprocessing import Pool from multiprocessing import Pool
from functools import partial from functools import partial
@click.command("init_db") @click.command("init_db")
@click.argument('drop_tables', nargs=1) @click.argument('drop_tables', nargs=1)
@with_appcontext @with_appcontext
@ -45,6 +46,7 @@ def init_accounts():
return return
@click.command("load_property") @click.command("load_property")
@click.argument('zone') @click.argument('zone')
@click.argument('player') @click.argument('player')
@ -56,7 +58,7 @@ def load_property(zone, player):
print("Character not Found") print("Character not Found")
return 404 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: if not prop:
print(f"Property {zone} not claimed by Character: {char.name}") print(f"Property {zone} not claimed by Character: {char.name}")
@ -95,6 +97,7 @@ def load_property(zone, player):
) )
new_prop_content.save() new_prop_content.save()
@click.command("gen_image_cache") @click.command("gen_image_cache")
def gen_image_cache(): def gen_image_cache():
"""generates image cache""" """generates image cache"""
@ -113,6 +116,7 @@ def gen_image_cache():
except BE: except BE:
return print(f"Error on {file}") return print(f"Error on {file}")
@click.command("gen_model_cache") @click.command("gen_model_cache")
def gen_model_cache(): def gen_model_cache():
"""generate model obj 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=1), files)
pool.map(partial(convert_lxfml_to_obj, lod=2), files) pool.map(partial(convert_lxfml_to_obj, lod=2), files)
def convert_lxfml_to_obj(file, lod): def convert_lxfml_to_obj(file, lod):
mtl = get_cache_file(file).with_suffix(f".lod{lod}.mtl") mtl = get_cache_file(file).with_suffix(f".lod{lod}.mtl")
if not mtl.exists(): if not mtl.exists():
mtl.parent.mkdir(parents=True, exist_ok=True) mtl.parent.mkdir(parents=True, exist_ok=True)
print(f"Convert LXFML {file.as_posix()} to obj and mtl @ {mtl}") print(f"Convert LXFML {file.as_posix()} to obj and mtl @ {mtl}")
try: 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: except Exception as e:
print(f"ERROR on {file}:\n {e}") print(f"ERROR on {file}:\n {e}")
else: else:
# print(f"Already Exists: {file} with LOD {lod}") # print(f"Already Exists: {file} with LOD {lod}")
return return
def get_cache_file(path): def get_cache_file(path):
"""helper""" """helper"""
# convert to list so that we can change elements # convert to list so that we can change elements
@ -147,6 +153,7 @@ def get_cache_file(path):
return pathlib.Path(*parts) return pathlib.Path(*parts)
def find_or_create_account(name, email, password, gm_level=9): def find_or_create_account(name, email, password, gm_level=9):
""" Find existing account or create new account """ """ Find existing account or create new account """
account = Account.query.filter(Account.email == email).first() 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() db.session.commit()
play_key = PlayKey.query.filter(PlayKey.key_string == key).first() play_key = PlayKey.query.filter(PlayKey.key_string == key).first()
account = Account(email=email, account = Account(
username=name, email=email,
password=current_app.user_manager.password_manager.hash_password(password), username=name,
play_key_id=play_key.id, password=current_app.user_manager.password_manager.hash_password(password),
email_confirmed_at=datetime.datetime.utcnow(), play_key_id=play_key.id,
gm_level=gm_level email_confirmed_at=datetime.datetime.utcnow(),
) gm_level=gm_level
)
play_key.key_uses = 0 play_key.key_uses = 0
db.session.add(account) db.session.add(account)
db.session.add(play_key) db.session.add(play_key)
db.session.commit() db.session.commit()
return # account return # account

View File

@ -16,13 +16,13 @@ from wtforms import (
SubmitField, SubmitField,
validators, validators,
IntegerField, IntegerField,
StringField,
SelectField SelectField
) )
from wtforms.validators import DataRequired, Optional from wtforms.validators import DataRequired, Optional
from app.models import PlayKey from app.models import PlayKey
def validate_play_key(form, field): def validate_play_key(form, field):
"""Validates a field for a valid phone number """Validates a field for a valid phone number
Args: Args:
@ -85,6 +85,7 @@ class CustomRegisterForm(FlaskForm):
submit = SubmitField('Register') submit = SubmitField('Register')
class CreatePlayKeyForm(FlaskForm): class CreatePlayKeyForm(FlaskForm):
count = IntegerField( count = IntegerField(
@ -97,6 +98,7 @@ class CreatePlayKeyForm(FlaskForm):
) )
submit = SubmitField('Create!') submit = SubmitField('Create!')
class EditPlayKeyForm(FlaskForm): class EditPlayKeyForm(FlaskForm):
active = BooleanField( active = BooleanField(
@ -119,7 +121,7 @@ class EditGMLevelForm(FlaskForm):
gm_level = IntegerField( gm_level = IntegerField(
'GM Level', 'GM Level',
widget=NumberInput(min = 0, max = 9) widget=NumberInput(min=0, max=9)
) )
submit = SubmitField('Submit') submit = SubmitField('Submit')
@ -142,8 +144,8 @@ class SendMailForm(FlaskForm):
'Recipient: ', 'Recipient: ',
coerce=str, coerce=str,
choices=[ choices=[
("",""), ("", ""),
("0","All Characters"), ("0", "All Characters"),
], ],
validators=[validators.DataRequired()] validators=[validators.DataRequired()]
) )
@ -162,7 +164,7 @@ class SendMailForm(FlaskForm):
attachment = SelectField( attachment = SelectField(
"Attachment", "Attachment",
coerce=str, coerce=str,
choices=[(0,"No Attachment")] choices=[(0, "No Attachment")]
) )
attachment_count = IntegerField( attachment_count = IntegerField(
@ -179,7 +181,7 @@ class RescueForm(FlaskForm):
'Move to:', 'Move to:',
coerce=str, coerce=str,
choices=[ choices=[
("",""), ("", ""),
], ],
validators=[validators.DataRequired()] validators=[validators.DataRequired()]
) )

View File

@ -1,5 +1,5 @@
from flask import render_template, Blueprint, request, url_for 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 app.models import CommandLog, ActivityLog, db, Account, CharacterInfo, AuditLog
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
import time import time
@ -7,6 +7,7 @@ from app import gm_level
log_blueprint = Blueprint('log', __name__) log_blueprint = Blueprint('log', __name__)
@log_blueprint.route('/activities', methods=['GET']) @log_blueprint.route('/activities', methods=['GET'])
@login_required @login_required
@gm_level(8) @gm_level(8)
@ -40,11 +41,11 @@ def audit():
@gm_level(8) @gm_level(8)
def get_activities(): def get_activities():
columns = [ columns = [
ColumnDT(ActivityLog.id), # 0 ColumnDT(ActivityLog.id), # 0
ColumnDT(ActivityLog.character_id), # 1 ColumnDT(ActivityLog.character_id), # 1
ColumnDT(ActivityLog.activity), # 2 ColumnDT(ActivityLog.activity), # 2
ColumnDT(ActivityLog.time), # 3 ColumnDT(ActivityLog.time), # 3
ColumnDT(ActivityLog.map_id), # 4 ColumnDT(ActivityLog.map_id), # 4
] ]
query = db.session.query().select_from(ActivityLog) query = db.session.query().select_from(ActivityLog)
@ -82,9 +83,9 @@ def get_activities():
@gm_level(8) @gm_level(8)
def get_commands(): def get_commands():
columns = [ columns = [
ColumnDT(CommandLog.id), # 0 ColumnDT(CommandLog.id), # 0
ColumnDT(CommandLog.character_id), # 1 ColumnDT(CommandLog.character_id), # 1
ColumnDT(CommandLog.command), # 2 ColumnDT(CommandLog.command), # 2
] ]
query = db.session.query().select_from(CommandLog) query = db.session.query().select_from(CommandLog)

View File

@ -19,6 +19,7 @@ import xml.etree.ElementTree as ET
luclient_blueprint = Blueprint('luclient', __name__) luclient_blueprint = Blueprint('luclient', __name__)
locale = {} locale = {}
@luclient_blueprint.route('/get_dds_as_png/<filename>') @luclient_blueprint.route('/get_dds_as_png/<filename>')
@login_required @login_required
def get_dds_as_png(filename): def get_dds_as_png(filename):
@ -37,7 +38,7 @@ def get_dds_as_png(filename):
with image.Image(filename=path) as img: with image.Image(filename=path) as img:
img.compression = "no" 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) return send_file(cache)
@ -69,7 +70,8 @@ def get_icon_lot(id):
)[0] )[0]
# find the asset from rendercomponent given the component id # 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], [render_component_id],
one=True one=True
)[0] )[0]
@ -117,6 +119,7 @@ def get_icon_iconid(id):
return send_file(pathlib.Path(cache).resolve()) return send_file(pathlib.Path(cache).resolve())
@luclient_blueprint.route('/unknown') @luclient_blueprint.route('/unknown')
@login_required @login_required
def unknown(): def unknown():
@ -195,6 +198,7 @@ def translate_from_locale(trans_string):
else: else:
return trans_string return trans_string
def register_luclient_jinja_helpers(app): def register_luclient_jinja_helpers(app):
@app.template_filter('get_zone_name') @app.template_filter('get_zone_name')
@ -203,15 +207,17 @@ def register_luclient_jinja_helpers(app):
@app.template_filter('get_skill_desc') @app.template_filter('get_skill_desc')
def get_skill_desc(skill_id): def get_skill_desc(skill_id):
return translate_from_locale(f'SkillBehavior_{skill_id}_descriptionUI').replace( return translate_from_locale(
"%(DamageCombo)", "Damage Combo: " f'SkillBehavior_{skill_id}_descriptionUI'
).replace( ).replace(
"%(AltCombo)", "<br/>Skeleton Combo: " "%(DamageCombo)", "Damage Combo: "
).replace( ).replace(
"%(Description)", "<br/>" "%(AltCombo)", "<br/>Skeleton Combo: "
).replace( ).replace(
"%(ChargeUp)", "<br/>Charge-up: " "%(Description)", "<br/>"
) ).replace(
"%(ChargeUp)", "<br/>Charge-up: "
)
@app.template_filter('parse_lzid') @app.template_filter('parse_lzid')
def parse_lzid(lzid): def parse_lzid(lzid):
@ -240,7 +246,7 @@ def register_luclient_jinja_helpers(app):
one=True one=True
) )
if intermed: 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 return name
@app.template_filter('get_lot_rarity') @app.template_filter('get_lot_rarity')
@ -252,7 +258,8 @@ def register_luclient_jinja_helpers(app):
one=True one=True
)[0] )[0]
rarity = query_cdclient('select rarity from ItemComponent where id = ?', rarity = query_cdclient(
'select rarity from ItemComponent where id = ?',
[render_component_id], [render_component_id],
one=True one=True
) )
@ -302,7 +309,6 @@ def register_luclient_jinja_helpers(app):
return consolidate_stats(stats) return consolidate_stats(stats)
@app.template_filter('get_set_stats') @app.template_filter('get_set_stats')
def get_set_stats(lot_id): def get_set_stats(lot_id):
stats = query_cdclient( stats = query_cdclient(
@ -331,7 +337,7 @@ def register_luclient_jinja_helpers(app):
def consolidate_stats(stats): def consolidate_stats(stats):
if len(stats) > 1: 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: for stat in stats:
if stat[0]: if stat[0]:
consolidated_stats["im"] += stat[0] consolidated_stats["im"] += stat[0]
@ -340,8 +346,7 @@ def consolidate_stats(stats):
if stat[2]: if stat[2]:
consolidated_stats["armor"] += stat[2] consolidated_stats["armor"] += stat[2]
if stat[3]: if stat[3]:
consolidated_stats["skill"].append([stat[3],stat[4]]) consolidated_stats["skill"].append([stat[3], stat[4]])
stats = consolidated_stats stats = consolidated_stats
elif len(stats) == 1: elif len(stats) == 1:

View File

@ -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 flask_user import login_required, current_user
from app.models import db, Mail, CharacterInfo from app.models import Mail, CharacterInfo
from datatables import ColumnDT, DataTables
from app.forms import SendMailForm from app.forms import SendMailForm
from app import gm_level, log_audit from app import gm_level, log_audit
from app.luclient import translate_from_locale, query_cdclient 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}") 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(): for character in CharacterInfo.query.all():
Mail( Mail(
sender_id = 0, sender_id=0,
sender_name = f"[GM] {current_user.username}", sender_name=f"[GM] {current_user.username}",
receiver_id = character.id, receiver_id=character.id,
receiver_name = character.name, receiver_name=character.name,
time_sent = time.time(), time_sent=time.time(),
subject = form.subject.data, subject=form.subject.data,
body = form.body.data, body=form.body.data,
attachment_id = 0, attachment_id=0,
attachment_lot = form.attachment.data, attachment_lot=form.attachment.data,
attachment_count = form.attachment_count.data attachment_count=form.attachment_count.data
).save() ).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: else:
Mail( Mail(
sender_id = 0, sender_id=0,
sender_name = f"[GM] {current_user.username}", sender_name=f"[GM] {current_user.username}",
receiver_id = form.recipient.data, receiver_id=form.recipient.data,
receiver_name = CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name, receiver_name=CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name,
time_sent = time.time(), time_sent=time.time(),
subject = form.subject.data, subject=form.subject.data,
body = form.body.data, body=form.body.data,
attachment_id = 0, attachment_id=0,
attachment_lot = form.attachment.data, attachment_lot=form.attachment.data,
attachment_count = form.attachment_count.data attachment_count=form.attachment_count.data
).save() ).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") flash("Sent Mail", "success")
return redirect(url_for('mail.send')) return redirect(url_for('mail.send'))
recipients = CharacterInfo.query.all() recipients = CharacterInfo.query.all()
for character in recipients: for character in recipients:
form.recipient.choices.append((character.id, character.name)) form.recipient.choices.append((character.id, character.name))
@ -74,7 +76,7 @@ def send():
for item in items: for item in items:
name = translate_from_locale(f'Objects_{item[0]}_name') name = translate_from_locale(f'Objects_{item[0]}_name')
if name == 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( form.attachment.choices.append(
( (
item[0], item[0],

View File

@ -1,18 +1,15 @@
from flask import render_template, Blueprint, redirect, request, send_from_directory, make_response, send_file, current_app from flask import render_template, Blueprint, send_from_directory
from flask_user import login_required, current_user from flask_user import current_user
import json, glob, os
from wand import image
from app.models import Account, AccountInvitation, CharacterInfo from app.models import Account
from app.schemas import AccountSchema, CharacterInfoSchema from app.schemas import AccountSchema, CharacterInfoSchema
from app.luclient import query_cdclient
from app import gm_level
main_blueprint = Blueprint('main', __name__) main_blueprint = Blueprint('main', __name__)
account_schema = AccountSchema() account_schema = AccountSchema()
char_info_schema = CharacterInfoSchema() char_info_schema = CharacterInfoSchema()
@main_blueprint.route('/', methods=['GET']) @main_blueprint.route('/', methods=['GET'])
def index(): def index():
"""Home/Index Page""" """Home/Index Page"""

View File

@ -1,6 +1,6 @@
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_user import UserMixin, current_user from flask_user import UserMixin
from wtforms import ValidationError from wtforms import ValidationError
import logging import logging
@ -12,6 +12,7 @@ from time import sleep
import random import random
import string import string
# retrying query to work around python trash collector # retrying query to work around python trash collector
# killing connections of other gunicorn workers # killing connections of other gunicorn workers
class RetryingQuery(BaseQuery): class RetryingQuery(BaseQuery):
@ -46,9 +47,11 @@ class RetryingQuery(BaseQuery):
raise raise
self.session.rollback() self.session.rollback()
db = SQLAlchemy(query_class=RetryingQuery) db = SQLAlchemy(query_class=RetryingQuery)
migrate = Migrate() migrate = Migrate()
class PlayKey(db.Model): class PlayKey(db.Model):
__tablename__ = 'play_keys' __tablename__ = 'play_keys'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -121,6 +124,7 @@ class PlayKey(db.Model):
db.session.commit() db.session.commit()
db.session.refresh(self) db.session.refresh(self)
class Account(db.Model, UserMixin): class Account(db.Model, UserMixin):
__tablename__ = 'accounts' __tablename__ = 'accounts'
id = db.Column( id = db.Column(
@ -200,7 +204,7 @@ class Account(db.Model, UserMixin):
@staticmethod @staticmethod
def get_user_by_id(*, user_id=None): 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): def save(self):
db.session.add(self) db.session.add(self)
@ -211,6 +215,7 @@ class Account(db.Model, UserMixin):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class AccountInvitation(db.Model): class AccountInvitation(db.Model):
__tablename__ = 'account_invites' __tablename__ = 'account_invites'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -245,14 +250,10 @@ class AccountInvitation(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
@staticmethod @staticmethod
def get_user_by_id(*, user_id=None): 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 # This table is cursed, see prop_clone_id
class CharacterInfo(db.Model): class CharacterInfo(db.Model):
@ -327,6 +328,7 @@ class CharacterInfo(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class CharacterXML(db.Model): class CharacterXML(db.Model):
__tablename__ = 'charxml' __tablename__ = 'charxml'
id = db.Column( id = db.Column(
@ -348,6 +350,7 @@ class CharacterXML(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class CommandLog(db.Model): class CommandLog(db.Model):
__tablename__ = 'command_log' __tablename__ = 'command_log'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -364,7 +367,7 @@ class CommandLog(db.Model):
passive_deletes=True passive_deletes=True
) )
command = db.Column( command = db.Column(
mysql.VARCHAR(256), mysql.VARCHAR(256),
nullable=False nullable=False
) )
@ -378,6 +381,7 @@ class CommandLog(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class Friends(db.Model): class Friends(db.Model):
__tablename__ = 'friends' __tablename__ = 'friends'
player_id = db.Column( player_id = db.Column(
@ -423,6 +427,7 @@ class Friends(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class Leaderboard(db.Model): class Leaderboard(db.Model):
__tablename__ = 'leaderboard' __tablename__ = 'leaderboard'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -472,6 +477,7 @@ class Leaderboard(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class Mail(db.Model): class Mail(db.Model):
__tablename__ = 'mail' __tablename__ = 'mail'
id = db.Column( id = db.Column(
@ -560,6 +566,7 @@ class Mail(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class ObjectIDTracker(db.Model): class ObjectIDTracker(db.Model):
__tablename__ = 'object_id_tracker' __tablename__ = 'object_id_tracker'
last_object_id = db.Column( last_object_id = db.Column(
@ -569,6 +576,7 @@ class ObjectIDTracker(db.Model):
server_default='0' server_default='0'
) )
class PetNames(db.Model): class PetNames(db.Model):
__tablename__ = 'pet_names' __tablename__ = 'pet_names'
id = db.Column(mysql.BIGINT, primary_key=True) id = db.Column(mysql.BIGINT, primary_key=True)
@ -582,7 +590,7 @@ class PetNames(db.Model):
server_default='0' server_default='0'
) )
owner_id = db.Column( owner_id = db.Column(
mysql.BIGINT, mysql.BIGINT,
nullable=True nullable=True
) )
@ -605,7 +613,7 @@ class Property(db.Model):
autoincrement=False autoincrement=False
) )
owner_id = db.Column( owner_id = db.Column(
mysql.BIGINT, mysql.BIGINT,
db.ForeignKey(CharacterInfo.id, ondelete='CASCADE'), db.ForeignKey(CharacterInfo.id, ondelete='CASCADE'),
nullable=False nullable=False
@ -623,7 +631,7 @@ class Property(db.Model):
nullable=False, nullable=False,
) )
clone_id = db.Column( clone_id = db.Column(
mysql.BIGINT(unsigned=True), mysql.BIGINT(unsigned=True),
db.ForeignKey(CharacterInfo.prop_clone_id, ondelete='CASCADE'), db.ForeignKey(CharacterInfo.prop_clone_id, ondelete='CASCADE'),
) )
@ -763,6 +771,7 @@ class UGC(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class PropertyContent(db.Model): class PropertyContent(db.Model):
__tablename__ = 'properties_contents' __tablename__ = 'properties_contents'
id = db.Column( id = db.Column(
@ -843,6 +852,7 @@ class PropertyContent(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class ActivityLog(db.Model): class ActivityLog(db.Model):
__tablename__ = 'activity_log' __tablename__ = 'activity_log'
id = db.Column(mysql.INTEGER, primary_key=True) id = db.Column(mysql.INTEGER, primary_key=True)
@ -883,6 +893,7 @@ class ActivityLog(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class BugReport(db.Model): class BugReport(db.Model):
__tablename__ = 'bug_reports' __tablename__ = 'bug_reports'
id = db.Column(mysql.INTEGER, primary_key=True) id = db.Column(mysql.INTEGER, primary_key=True)
@ -944,6 +955,7 @@ class BugReport(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class Server(db.Model): class Server(db.Model):
__tablename__ = 'servers' __tablename__ = 'servers'
id = db.Column( id = db.Column(
@ -1016,6 +1028,7 @@ class Reports(db.Model):
db.session.delete(self) db.session.delete(self)
db.session.commit() db.session.commit()
class AuditLog(db.Model): class AuditLog(db.Model):
__tablename__ = 'audit_logs' __tablename__ = 'audit_logs'
id = db.Column( id = db.Column(

View File

@ -2,7 +2,6 @@ from flask import render_template, Blueprint, redirect, url_for, request, flash,
from flask_user import login_required from flask_user import login_required
from app.models import PetNames, db, CharacterXML, CharacterInfo from app.models import PetNames, db, CharacterXML, CharacterInfo
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
from app.forms import CreatePlayKeyForm, EditPlayKeyForm
from app import gm_level, log_audit, scheduler from app import gm_level, log_audit, scheduler
moderation_blueprint = Blueprint('moderation', __name__) moderation_blueprint = Blueprint('moderation', __name__)
@ -20,7 +19,7 @@ def index(status):
@gm_level(3) @gm_level(3)
def approve_pet(id): 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 pet_data.approved = 2
log_audit(f"Approved pet name {pet_data.pet_name} from {pet_data.owner_id}") 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) @gm_level(3)
def reject_pet(id): 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 pet_data.approved = 0
log_audit(f"Rejected pet name {pet_data.pet_name} from {pet_data.owner_id}") 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 query = None
if status=="all": if status == "all":
query = db.session.query().select_from(PetNames) query = db.session.query().select_from(PetNames)
elif status=="approved": elif status == "approved":
query = db.session.query().select_from(PetNames).filter(PetNames.approved==2) query = db.session.query().select_from(PetNames).filter(PetNames.approved == 2)
elif status=="unapproved": elif status == "unapproved":
query = db.session.query().select_from(PetNames).filter(PetNames.approved==1) query = db.session.query().select_from(PetNames).filter(PetNames.approved == 1)
else: else:
raise Exception("Not a valid filter") raise Exception("Not a valid filter")
params = request.args.to_dict() params = request.args.to_dict()
rowTable = DataTables(params, query, columns) rowTable = DataTables(params, query, columns)
@ -116,8 +114,8 @@ def get_pets(status="all"):
{CharacterInfo.query.filter(CharacterInfo.id==pet_data['3']).first().name} {CharacterInfo.query.filter(CharacterInfo.id==pet_data['3']).first().name}
</a> </a>
""" """
except Exception as e: except Exception:
PetNames.query.filter(PetNames.id==id).first().delete() PetNames.query.filter(PetNames.id == id).first().delete()
pet_data["0"] = "<span class='text-danger'>Deleted Refresh to make go away</span>" pet_data["0"] = "<span class='text-danger'>Deleted Refresh to make go away</span>"
pet_data["3"] = "<span class='text-danger'>Character Deleted</span>" pet_data["3"] = "<span class='text-danger'>Character Deleted</span>"
else: else:
@ -131,7 +129,7 @@ def pet_name_maintenance():
with scheduler.app.app_context(): with scheduler.app.app_context():
# associate pet names to characters # associate pet names to characters
current_app.logger.info("Started Pet Name Maintenance") 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: if unassociated_pets:
current_app.logger.info("Found un-associated pets") current_app.logger.info("Found un-associated pets")
for pet in unassociated_pets: for pet in unassociated_pets:
@ -143,11 +141,11 @@ def pet_name_maintenance():
pet.delete() pet.delete()
# auto-moderate based on already moderated names # 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: if unmoderated_pets:
current_app.logger.info("Found un-moderated Pets") current_app.logger.info("Found un-moderated Pets")
for pet in unmoderated_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: if existing_pet:
pet.approved = existing_pet.approved pet.approved = existing_pet.approved
pet.save() pet.save()

View File

@ -1,14 +1,14 @@
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 flask_user import login_required
from app.models import Account, AccountInvitation, PlayKey, db from app.models import Account, PlayKey, db
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
from app.forms import CreatePlayKeyForm, EditPlayKeyForm from app.forms import CreatePlayKeyForm, EditPlayKeyForm
from app import gm_level, log_audit from app import gm_level, log_audit
play_keys_blueprint = Blueprint('play_keys', __name__) play_keys_blueprint = Blueprint('play_keys', __name__)
# Key creation page
# Key creation page
@play_keys_blueprint.route('/', methods=['GET']) @play_keys_blueprint.route('/', methods=['GET'])
@login_required @login_required
@gm_level(9) @gm_level(9)
@ -45,7 +45,7 @@ def bulk_create():
@gm_level(9) @gm_level(9)
def delete(id): def delete(id):
key = PlayKey.query.filter(PlayKey.id == id).first() 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}") log_audit(f"Deleted Play Key {key.key_string}")
flash(f"Deleted Play Key {key.key_string}", "danger") flash(f"Deleted Play Key {key.key_string}", "danger")
key.delete() key.delete()
@ -56,7 +56,7 @@ def delete(id):
@login_required @login_required
@gm_level(9) @gm_level(9)
def edit(id): def edit(id):
key = PlayKey.query.filter(PlayKey.id==id).first() key = PlayKey.query.filter(PlayKey.id == id).first()
form = EditPlayKeyForm() form = EditPlayKeyForm()
if form.validate_on_submit(): if form.validate_on_submit():
@ -84,7 +84,7 @@ def edit(id):
@gm_level(9) @gm_level(9)
def view(id): def view(id):
key = PlayKey.query.filter(PlayKey.id == id).first() 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) return render_template('play_keys/view.html.j2', key=key, accounts=accounts)
@ -125,7 +125,12 @@ def get():
Delete Delete
</a> </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-dialog modal-lg" role="document">
<div class="modal-content bg-dark border-primary"> <div class="modal-content bg-dark border-primary">
<div class="modal-header"> <div class="modal-header">

View File

@ -5,14 +5,11 @@ from flask import (
url_for, url_for,
request, request,
abort, abort,
jsonify,
send_from_directory,
make_response, make_response,
flash, flash,
current_app current_app
) )
from flask_user import login_required, current_user from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables from datatables import ColumnDT, DataTables
import time import time
from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account 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 from app.luclient import query_cdclient
import zlib import zlib
import xmltodict
import os
import app.pylddlib as ldd import app.pylddlib as ldd
import pathlib import pathlib
@ -30,6 +25,7 @@ property_blueprint = Blueprint('properties', __name__)
property_schema = PropertySchema() property_schema = PropertySchema()
@property_blueprint.route('/', methods=['GET']) @property_blueprint.route('/', methods=['GET'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -42,7 +38,7 @@ def index():
@gm_level(3) @gm_level(3)
def approve(id): 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 property_data.mod_approved = not property_data.mod_approved
@ -89,8 +85,6 @@ def approve(id):
else: else:
go_to = url_for('main.index') go_to = url_for('main.index')
return redirect(go_to) return redirect(go_to)
@ -134,16 +128,19 @@ def get(status="all"):
] ]
query = None query = None
if status=="all": if status == "all":
query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account) query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id == Property.owner_id).join(Account)
elif status=="approved": 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) query = db.session.query().select_from(Property).join(
elif status=="unapproved": CharacterInfo, CharacterInfo.id == Property.owner_id
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) ).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: else:
raise Exception("Not a valid filter") raise Exception("Not a valid filter")
params = request.args.to_dict() params = request.args.to_dict()
rowTable = DataTables(params, query, columns) rowTable = DataTables(params, query, columns)
@ -183,10 +180,10 @@ def get(status="all"):
if property_data["4"] == "": if property_data["4"] == "":
property_data["4"] = query_cdclient( property_data["4"] = query_cdclient(
'select DisplayDescription from ZoneTable where zoneID = ?', 'select DisplayDescription from ZoneTable where zoneID = ?',
[property_data["12"]], [property_data["12"]],
one=True one=True
) )
if property_data["6"] == 0: if property_data["6"] == 0:
property_data["6"] = "Private" property_data["6"] = "Private"
@ -215,7 +212,7 @@ def get(status="all"):
@property_blueprint.route('/view_model/<id>/<lod>', methods=['GET']) @property_blueprint.route('/view_model/<id>/<lod>', methods=['GET'])
@login_required @login_required
def view_model(id, lod): 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 # TODO: Restrict somehow
formatted_data = [ formatted_data = [
@ -242,6 +239,7 @@ def view_model(id, lod):
lod=lod lod=lod
) )
property_center = { property_center = {
1150: "(-17, 432, -60)", 1150: "(-17, 432, -60)",
1151: "(0, 455, -110)", 1151: "(0, 455, -110)",
@ -256,7 +254,7 @@ property_center = {
@login_required @login_required
def view_models(id, lod): def view_models(id, lod):
property_content_data = PropertyContent.query.filter( property_content_data = PropertyContent.query.filter(
PropertyContent.property_id==id PropertyContent.property_id == id
).order_by(PropertyContent.lot).all() ).order_by(PropertyContent.lot).all()
consolidated_list = [] 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( return render_template(
'ldd/ldd.html.j2', 'ldd/ldd.html.j2',
property_data=property_data, property_data=property_data,
@ -304,30 +302,30 @@ def view_models(id, lod):
lod=lod lod=lod
) )
@property_blueprint.route('/get_model/<id>/<file_format>/<lod>', methods=['GET']) @property_blueprint.route('/get_model/<id>/<file_format>/<lod>', methods=['GET'])
@login_required @login_required
def get_model(id, file_format, lod): 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): if not(0 <= int(lod) <= 2):
abort(404) abort(404)
if content.lot == 14: # ugc model if content.lot == 14: # ugc model
response = ugc(content)[0] response = ugc(content)[0]
else: # prebuild model else: # prebuilt model
response = prebuilt(content, file_format, lod)[0] response = prebuilt(content, file_format, lod)[0]
response.headers.set('Content-Type', 'text/xml') response.headers.set('Content-Type', 'text/xml')
return response return response
@property_blueprint.route('/download_model/<id>', methods=['GET']) @property_blueprint.route('/download_model/<id>', methods=['GET'])
@login_required @login_required
def download_model(id): 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) response, filename = ugc(content)
else: # prebuild model else: # prebuilt model
response, filename = prebuilt(content, "lxfml") response, filename = prebuilt(content, "lxfml")
response.headers.set('Content-Type', 'attachment/xml') response.headers.set('Content-Type', 'attachment/xml')
@ -340,7 +338,7 @@ def download_model(id):
def ugc(content): 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) uncompressed_lxfml = zlib.decompress(ugc_data.lxfml)
response = make_response(uncompressed_lxfml) response = make_response(uncompressed_lxfml)
return response, ugc_data.filename return response, ugc_data.filename
@ -355,7 +353,8 @@ def prebuilt(content, file_format, lod):
one=True one=True
)[0] )[0]
# find the asset from rendercomponent given the component id # 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], [render_component_id],
one=True 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 = pathlib.Path(f'app/cache/BrickModels/{filename}.lod{lod}.{file_format}')
cache.parent.mkdir(parents=True, exist_ok=True) cache.parent.mkdir(parents=True, exist_ok=True)
try: 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: except Exception as e:
current_app.logger.error(f"ERROR on {cache}:\n {e}") current_app.logger.error(f"ERROR on {cache}:\n {e}")

View File

@ -1,33 +1,12 @@
#!/usr/bin/env python #!/usr/bin/env python
# pylddlib version 0.4.9.7 # pylddlib version 0.4.9.7
# based on pyldd2obj version 0.4.8 - Copyright (c) 2019 by jonnysp # 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 os
import platform
import sys import sys
import math import math
import struct import struct
import zipfile import zipfile
from xml.dom import minidom from xml.dom import minidom
import time
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf-8')
PRIMITIVEPATH = '/Primitives/' PRIMITIVEPATH = '/Primitives/'
GEOMETRIEPATH = PRIMITIVEPATH 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"} LOGOONSTUDSCONNTYPE = {"0:4", "0:4:1", "0:4:2", "0:4:33", "2:4:1", "2:4:34"}
class Matrix3D: 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.n11 = n11
self.n12 = n12 self.n12 = n12
self.n13 = n13 self.n13 = n13
@ -56,9 +36,26 @@ class Matrix3D:
self.n44 = n44 self.n44 = n44
def __str__(self): 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) c = math.cos(angle)
s = math.sin(angle) s = math.sin(angle)
t = 1 - c 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.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.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 self.n14 * other.n41 + self.n24 * other.n42 + self.n34 * other.n43 + self.n44 * other.n44
) )
class Point3D: class Point3D:
def __init__(self, x=0,y=0,z=0): def __init__(self, x=0, y=0, z=0):
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
def __str__(self): 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"): def string(self, prefix="v"):
return '{0} {1:f} {2:f} {3:f}\n'.format(prefix ,self.x , self.y, self.z) 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 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 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 z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z
@ -131,7 +129,7 @@ class Point3D:
self.y = y self.y = y
self.z = z 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 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 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 z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z + matrix.n43
@ -140,41 +138,58 @@ class Point3D:
self.z = z self.z = z
def copy(self): 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: class Point2D:
def __init__(self, x=0,y=0): def __init__(self, x=0, y=0):
self.x = x self.x = x
self.y = y self.y = y
def __str__(self): def __str__(self):
return '[{0},{1}]'.format(self.x, self.y * -1) 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): def copy(self):
return Point2D(x=self.x,y=self.y) return Point2D(x=self.x, y=self.y)
class Face: class Face:
def __init__(self,a=0,b=0,c=0): def __init__(self, a=0, b=0, c=0):
self.a = a self.a = a
self.b = b self.b = b
self.c = c self.c = c
def string(self,prefix="f", indexOffset=0 ,textureoffset=0):
def string(self, prefix="f", indexOffset=0, textureoffset=0):
if textureoffset == 0: if textureoffset == 0:
return prefix + ' {0}//{0} {1}//{1} {2}//{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset) return prefix + ' {0}//{0} {1}//{1} {2}//{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset)
else: 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): def __str__(self):
return '[{0},{1},{2}]'.format(self.a, self.b, self.c) return '[{0},{1},{2}]'.format(self.a, self.b, self.c)
class Group: class Group:
def __init__(self, node): def __init__(self, node):
self.partRefs = node.getAttribute('partRefs').split(',') self.partRefs = node.getAttribute('partRefs').split(',')
class Bone: class Bone:
def __init__(self, node): def __init__(self, node):
self.refID = node.getAttribute('refID') self.refID = node.getAttribute('refID')
(a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(',')) (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: class Part:
def __init__(self, node): def __init__(self, node):
@ -185,19 +200,17 @@ class Part:
self.designID = node.getAttribute('designID') self.designID = node.getAttribute('designID')
self.materials = list(map(str, node.getAttribute('materials').split(','))) self.materials = list(map(str, node.getAttribute('materials').split(',')))
lastm = '0'
for i, m in enumerate(self.materials): for i, m in enumerate(self.materials):
if (m == '0'): if (m == '0'):
# self.materials[i] = lastm # self.materials[i] = lastm
self.materials[i] = self.materials[0] #in case of 0 choose the 'base' material self.materials[i] = self.materials[0] # in case of 0 choose the 'base' material
else:
lastm = m
if node.hasAttribute('decoration'): 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: for childnode in node.childNodes:
if childnode.nodeName == 'Bone': if childnode.nodeName == 'Bone':
self.Bones.append(Bone(node=childnode)) self.Bones.append(Bone(node=childnode))
class Brick: class Brick:
def __init__(self, node): def __init__(self, node):
self.refID = node.getAttribute('refID') self.refID = node.getAttribute('refID')
@ -207,14 +220,16 @@ class Brick:
if childnode.nodeName == 'Part': if childnode.nodeName == 'Part':
self.Parts.append(Part(node=childnode)) self.Parts.append(Part(node=childnode))
class SceneCamera: class SceneCamera:
def __init__(self, node): def __init__(self, node):
self.refID = node.getAttribute('refID') self.refID = node.getAttribute('refID')
(a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(',')) (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.fieldOfView = float(node.getAttribute('fieldOfView'))
self.distance = float(node.getAttribute('distance')) self.distance = float(node.getAttribute('distance'))
class Scene: class Scene:
def __init__(self, file): def __init__(self, file):
self.Bricks = [] self.Bricks = []
@ -262,6 +277,7 @@ class Scene:
# print('Scene "'+ self.Name + '" Brickversion: ' + str(self.Version)) # print('Scene "'+ self.Name + '" Brickversion: ' + str(self.Version))
class GeometryReader: class GeometryReader:
def __init__(self, data): def __init__(self, data):
self.offset = 0 self.offset = 0
@ -282,10 +298,10 @@ class GeometryReader:
options = self.readInt() options = self.readInt()
for i in range(0, self.valueCount): 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): 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: if (options & 3) == 3:
self.texCount = self.valueCount self.texCount = self.valueCount
@ -293,7 +309,7 @@ class GeometryReader:
self.textures.append(Point2D(x=self.readFloat(), y=self.readFloat())) self.textures.append(Point2D(x=self.readFloat(), y=self.readFloat()))
for i in range(0, self.faceCount): 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: if (options & 48) == 48:
num = self.readInt() num = self.readInt()
@ -311,7 +327,7 @@ class GeometryReader:
boneoffset = self.readInt() + 4 boneoffset = self.readInt() + 4
self.bonemap[i] = self.read_Int(datastart + boneoffset) self.bonemap[i] = self.read_Int(datastart + boneoffset)
def read_Int(self,_offset): def read_Int(self, _offset):
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
return int(struct.unpack_from('i', self.data, _offset)[0]) return int(struct.unpack_from('i', self.data, _offset)[0])
else: else:
@ -330,6 +346,7 @@ class GeometryReader:
self.offset += 4 self.offset += 4
return ret return ret
class Geometry: class Geometry:
def __init__(self, designID, database): def __init__(self, designID, database):
self.designID = designID self.designID = designID
@ -337,21 +354,25 @@ class Geometry:
self.maxGeoBounding = -1 self.maxGeoBounding = -1
self.studsFields2D = [] self.studsFields2D = []
GeometryLocation = '{0}{1}{2}'.format(GEOMETRIEPATH, designID,'.g') GeometryLocation = '{0}{1}{2}'.format(GEOMETRIEPATH, designID, '.g')
GeometryCount = 0 GeometryCount = 0
while str(GeometryLocation) in database.filelist: while str(GeometryLocation) in database.filelist:
self.Parts[GeometryCount] = GeometryReader(data=database.filelist[GeometryLocation].read()) self.Parts[GeometryCount] = GeometryReader(data=database.filelist[GeometryLocation].read())
GeometryCount += 1 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.Partname = primitive.Designname
self.studsFields2D = primitive.Fields2D self.studsFields2D = primitive.Fields2D
try: 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() geoBoundingList.sort()
self.maxGeoBounding = geoBoundingList[-1] self.maxGeoBounding = geoBoundingList[-1]
except KeyError as e: except KeyError:
# print('\nBounding errror in part {0}: {1}\n'.format(designID, e)) # print('\nBounding errror in part {0}: {1}\n'.format(designID, e))
pass pass
@ -386,25 +407,33 @@ class Geometry:
count += self.Parts[part].texCount count += self.Parts[part].texCount
return count return count
class Bone2: 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 self.boneId = boneId
rotationMatrix = Matrix3D() rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0,axis = Point3D(x=ax,y=ay,z=az)) rotationMatrix.rotate(
p = Point3D(x=tx,y=ty,z=tz) 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) p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x rotationMatrix.n41 -= p.x
rotationMatrix.n42 -= p.y rotationMatrix.n42 -= p.y
rotationMatrix.n43 -= p.z rotationMatrix.n43 -= p.z
self.matrix = rotationMatrix self.matrix = rotationMatrix
class Field2D: 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'): 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.type = type
self.field2DRawData = field2DRawData self.field2DRawData = field2DRawData
rotationMatrix = Matrix3D() rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az)) rotationMatrix.rotate(
p = Point3D(x=tx,y=ty,z=tz) 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) p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x rotationMatrix.n41 -= p.x
rotationMatrix.n42 -= p.y rotationMatrix.n42 -= p.y
@ -413,7 +442,7 @@ class Field2D:
self.matrix = rotationMatrix self.matrix = rotationMatrix
self.custom2DField = [] 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 rows_count = height + 1
cols_count = width + 1 cols_count = width + 1
# creation looks reverse # creation looks reverse
@ -432,18 +461,23 @@ class Field2D:
def __str__(self): def __str__(self):
return '[type="{0}" transform="{1}" custom2DField="{2}"]'.format(self.type, self.matrix, self.custom2DField) return '[type="{0}" transform="{1}" custom2DField="{2}"]'.format(self.type, self.matrix, self.custom2DField)
class CollisionBox: 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): 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 = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az)) rotationMatrix.rotate(
p = Point3D(x=tx,y=ty,z=tz) 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) p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x rotationMatrix.n41 -= p.x
rotationMatrix.n42 -= p.y rotationMatrix.n42 -= p.y
rotationMatrix.n43 -= p.z rotationMatrix.n43 -= p.z
self.matrix = rotationMatrix 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 = []
self.positions.append(Point3D(x=0, y=0, z=0)) 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=sX, y=sY, z=0))
self.positions.append(Point3D(x=0, y=0, z=sZ)) 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=0, y=sY, z=sZ))
self.positions.append(Point3D(x=sX ,y=0, 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=sY, z=sZ))
def __str__(self): 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: class Primitive:
def __init__(self, data): def __init__(self, data):
@ -475,7 +512,18 @@ class Primitive:
if node.nodeName == 'Flex': if node.nodeName == 'Flex':
for node in node.childNodes: for node in node.childNodes:
if node.nodeName == 'Bone': 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': elif node.nodeName == 'Annotations':
for childnode in node.childNodes: for childnode in node.childNodes:
if childnode.nodeName == 'Annotation' and childnode.hasAttribute('designname'): if childnode.nodeName == 'Annotation' and childnode.hasAttribute('designname'):
@ -483,23 +531,73 @@ class Primitive:
elif node.nodeName == 'Collision': elif node.nodeName == 'Collision':
for childnode in node.childNodes: for childnode in node.childNodes:
if childnode.nodeName == 'Box': 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': 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': elif node.nodeName == 'Bounding':
for childnode in node.childNodes: for childnode in node.childNodes:
if childnode.nodeName == 'AABB': 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': elif node.nodeName == 'GeometryBounding':
for childnode in node.childNodes: for childnode in node.childNodes:
if childnode.nodeName == 'AABB': 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': elif node.nodeName == 'Connectivity':
for childnode in node.childNodes: for childnode in node.childNodes:
if childnode.nodeName == 'Custom2DField': 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': 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: class Materials:
def __init__(self, data): def __init__(self, data):
@ -514,13 +612,14 @@ class Materials:
b=int(node.getAttribute('Blue')), b=int(node.getAttribute('Blue')),
a=int(node.getAttribute('Alpha')), a=int(node.getAttribute('Alpha')),
mtype=str(node.getAttribute('MaterialType')) mtype=str(node.getAttribute('MaterialType'))
) )
def getMaterialbyId(self, mid): def getMaterialbyId(self, mid):
return self.Materials[mid] return self.Materials[mid]
class Material: class Material:
def __init__(self,id, r, g, b, a, mtype): def __init__(self, id, r, g, b, a, mtype):
self.id = id self.id = id
self.name = id self.name = id
self.mattype = mtype self.mattype = mtype
@ -528,18 +627,25 @@ class Material:
self.g = float(g) self.g = float(g)
self.b = float(b) self.b = float(b)
self.a = float(a) self.a = float(a)
def string(self): 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: if self.a < 255:
out += 'Ni 1.575\n' + 'd {0}'.format(0.05) + '\n' + 'Tr {0}\n'.format(0.05) out += 'Ni 1.575\n' + 'd {0}'.format(0.05) + '\n' + 'Tr {0}\n'.format(0.05)
return out return out
class DBinfo: class DBinfo:
def __init__(self, data): def __init__(self, data):
xml = minidom.parseString(data) xml = minidom.parseString(data)
self.Version = xml.getElementsByTagName('Bricks')[0].attributes['version'].value self.Version = xml.getElementsByTagName('Bricks')[0].attributes['version'].value
# print('DB Version: ' + str(self.Version)) # print('DB Version: ' + str(self.Version))
class DBFolderFile: class DBFolderFile:
def __init__(self, name, handle): def __init__(self, name, handle):
self.handle = handle self.handle = handle
@ -554,6 +660,7 @@ class DBFolderFile:
finally: finally:
reader.close() reader.close()
class LIFFile: class LIFFile:
def __init__(self, name, offset, size, handle): def __init__(self, name, offset, size, handle):
self.handle = handle self.handle = handle
@ -565,6 +672,7 @@ class LIFFile:
self.handle.seek(self.offset, 0) self.handle.seek(self.offset, 0)
return self.handle.read(self.size) return self.handle.read(self.size)
class DBFolderReader: class DBFolderReader:
def __init__(self, folder): def __init__(self, folder):
self.filelist = {} self.filelist = {}
@ -574,14 +682,14 @@ class DBFolderReader:
try: try:
os.path.isdir(self.location) os.path.isdir(self.location)
except Exception as e: except Exception:
self.initok = False self.initok = False
# print("db folder read FAIL") # print("db folder read FAIL")
return return
else: else:
self.parse() self.parse()
if self.fileexist(os.path.join(self.location,'Materials.xml')) and self.fileexist(os.path.join(self.location, 'info.xml')): 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()) self.dbinfo = DBinfo(data=self.filelist[os.path.join(self.location, 'info.xml')].read())
# print("DB folder OK.") # print("DB folder OK.")
self.initok = True self.initok = True
else: else:
@ -593,7 +701,6 @@ class DBFolderReader:
# print(MATERIALNAMESPATH) # print(MATERIALNAMESPATH)
pass pass
def fileexist(self, filename): def fileexist(self, filename):
return filename in self.filelist return filename in self.filelist
@ -603,6 +710,7 @@ class DBFolderReader:
entryName = os.path.join(path, name) entryName = os.path.join(path, name)
self.filelist[entryName] = DBFolderFile(name=entryName, handle=entryName) self.filelist[entryName] = DBFolderFile(name=entryName, handle=entryName)
class LIFReader: class LIFReader:
def __init__(self, file): def __init__(self, file):
self.packedFilesOffset = 84 self.packedFilesOffset = 84
@ -614,7 +722,7 @@ class LIFReader:
try: try:
self.filehandle = open(self.location, "rb") self.filehandle = open(self.location, "rb")
self.filehandle.seek(0, 0) self.filehandle.seek(0, 0)
except Exception as e: except Exception:
self.initok = False self.initok = False
# print("Database FAIL") # print("Database FAIL")
return return
@ -632,7 +740,7 @@ class LIFReader:
# print("Database FAIL") # print("Database FAIL")
self.initok = False self.initok = False
def fileexist(self,filename): def fileexist(self, filename):
return filename in self.filelist return filename in self.filelist
def parse(self, prefix='', offset=0): def parse(self, prefix='', offset=0):
@ -648,7 +756,7 @@ class LIFReader:
entryType = self.readShort(offset=offset) entryType = self.readShort(offset=offset)
offset += 6 offset += 6
entryName = '{0}{1}'.format(prefix,'/'); entryName = '{0}{1}'.format(prefix, '/')
self.filehandle.seek(offset + 1, 0) self.filehandle.seek(offset + 1, 0)
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
t = ord(self.filehandle.read(1)) t = ord(self.filehandle.read(1))
@ -656,7 +764,7 @@ class LIFReader:
t = int.from_bytes(self.filehandle.read(1), byteorder='big') t = int.from_bytes(self.filehandle.read(1), byteorder='big')
while not t == 0: while not t == 0:
entryName ='{0}{1}'.format(entryName,chr(t)) entryName = '{0}{1}'.format(entryName, chr(t))
self.filehandle.seek(1, 1) self.filehandle.seek(1, 1)
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
t = ord(self.filehandle.read(1)) t = ord(self.filehandle.read(1))
@ -692,33 +800,32 @@ class LIFReader:
else: else:
return int.from_bytes(self.filehandle.read(2), byteorder='big') return int.from_bytes(self.filehandle.read(2), byteorder='big')
class Converter: class Converter:
def LoadDBFolder(self, dbfolderlocation): def LoadDBFolder(self, dbfolderlocation):
self.database = DBFolderReader(folder=dbfolderlocation) self.database = DBFolderReader(folder=dbfolderlocation)
if self.database.initok and self.database.fileexist(os.path.join(dbfolderlocation,'Materials.xml')): 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()); 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) self.database = LIFReader(file=databaselocation)
if self.database.initok and self.database.fileexist('/Materials.xml'): 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: if self.database.initok:
self.scene = Scene(file=filename) self.scene = Scene(file=filename)
def Export(self,filename): def Export(self, filename):
invert = Matrix3D() invert = Matrix3D()
#invert.n33 = -1 #uncomment to invert the Z-Axis # invert.n33 = -1 #uncomment to invert the Z-Axis
indexOffset = 1 indexOffset = 1
textOffset = 1 textOffset = 1
usedmaterials = [] usedmaterials = []
geometriecache = {} geometriecache = {}
start_time = time.time()
out = open(filename + ".obj.tmp", "w+") out = open(filename + ".obj.tmp", "w+")
out.truncate(0) out.truncate(0)
out.write("mtllib " + filename + ".mtl" + '\n\n') out.write("mtllib " + filename + ".mtl" + '\n\n')
@ -735,12 +842,12 @@ class Converter:
if pa.designID not in geometriecache: if pa.designID not in geometriecache:
geo = Geometry(designID=pa.designID, database=self.database) 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 geometriecache[pa.designID] = geo
else: else:
geo = geometriecache[pa.designID] geo = geometriecache[pa.designID]
progress(current ,total , "(" + geo.designID + ") " + geo.Partname ,'-') progress(current, total, "(" + geo.designID + ") " + geo.Partname, '-')
out.write("o\n") out.write("o\n")
@ -752,11 +859,11 @@ class Converter:
# positions # positions
for j, p in enumerate(geo.Parts[part].outpositions): for j, p in enumerate(geo.Parts[part].outpositions):
if (geo.Parts[part].bonemap[j] == i): if (geo.Parts[part].bonemap[j] == i):
p.transform( invert * b.matrix) p.transform(invert * b.matrix)
# normals # normals
for k, n in enumerate(geo.Parts[part].outnormals): for k, n in enumerate(geo.Parts[part].outnormals):
if (geo.Parts[part].bonemap[k] == i): if (geo.Parts[part].bonemap[k] == i):
n.transformW( invert * b.matrix) n.transformW(invert * b.matrix)
for point in geo.Parts[part].outpositions: for point in geo.Parts[part].outpositions:
out.write(point.string("v")) out.write(point.string("v"))
@ -772,13 +879,12 @@ class Converter:
last_color = 0 last_color = 0
for part in geo.Parts: 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: try:
materialCurrentPart = pa.materials[part] materialCurrentPart = pa.materials[part]
last_color = pa.materials[part] last_color = pa.materials[part]
except IndexError: 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 materialCurrentPart = last_color
lddmat = self.allMaterials.getMaterialbyId(materialCurrentPart) lddmat = self.allMaterials.getMaterialbyId(materialCurrentPart)
@ -786,7 +892,7 @@ class Converter:
deco = '0' deco = '0'
if hasattr(pa, 'decoration') and len(geo.Parts[part].textures) > 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): if decoCount < len(pa.decoration):
deco = pa.decoration[decoCount] deco = pa.decoration[decoCount]
decoCount += 1 decoCount += 1
@ -801,7 +907,7 @@ class Converter:
f.write(self.database.filelist[decofilename].read()) f.write(self.database.filelist[decofilename].read())
f.close() f.close()
if not matname in usedmaterials: if matname not in usedmaterials:
usedmaterials.append(matname) usedmaterials.append(matname)
outtext.write("newmtl " + matname + '\n') outtext.write("newmtl " + matname + '\n')
outtext.write(lddmat.string()) outtext.write(lddmat.string())
@ -811,9 +917,9 @@ class Converter:
out.write("usemtl " + matname + '\n') out.write("usemtl " + matname + '\n')
for face in geo.Parts[part].faces: for face in geo.Parts[part].faces:
if len(geo.Parts[part].textures) > 0: if len(geo.Parts[part].textures) > 0:
out.write(face.string("f",indexOffset,textOffset)) out.write(face.string("f", indexOffset, textOffset))
else: else:
out.write(face.string("f",indexOffset)) out.write(face.string("f", indexOffset))
indexOffset += len(geo.Parts[part].outpositions) indexOffset += len(geo.Parts[part].outpositions)
textOffset += len(geo.Parts[part].textures) textOffset += len(geo.Parts[part].textures)
@ -825,6 +931,7 @@ class Converter:
sys.stdout.write('%s\r' % (' ')) sys.stdout.write('%s\r' % (' '))
# print("--- %s seconds ---" % (time.time() - start_time)) # print("--- %s seconds ---" % (time.time() - start_time))
def setDBFolderVars(dbfolderlocation, lod): def setDBFolderVars(dbfolderlocation, lod):
global PRIMITIVEPATH global PRIMITIVEPATH
global GEOMETRIEPATH global GEOMETRIEPATH
@ -836,44 +943,8 @@ def setDBFolderVars(dbfolderlocation, lod):
MATERIALNAMESPATH = os.path.join(dbfolderlocation, 'MaterialNames', '') MATERIALNAMESPATH = os.path.join(dbfolderlocation, 'MaterialNames', '')
# print(MATERIALNAMESPATH) # 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. def progress(count, total, status='', suffix=''):
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 = ''):
bar_len = 40 bar_len = 40
filled_len = int(round(bar_len * count / float(total))) filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1) 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.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, status))
sys.stdout.flush() sys.stdout.flush()
def main(lxf_filename, obj_filename, lod="2"): def main(lxf_filename, obj_filename, lod="2"):
# print("- - - pylddlib - - -") # print("- - - pylddlib - - -")
# print(" _ ") # print(" _ ")
@ -896,10 +968,11 @@ def main(lxf_filename, obj_filename, lod="2"):
GEOMETRIEPATH = GEOMETRIEPATH + f"LOD{lod}/" GEOMETRIEPATH = GEOMETRIEPATH + f"LOD{lod}/"
converter = Converter() converter = Converter()
# print("Found DB folder. Will use this instead of db.lif!") # print("Found DB folder. Will use this instead of db.lif!")
setDBFolderVars(dbfolderlocation = "app/luclient/res/", lod=lod) setDBFolderVars(dbfolderlocation="app/luclient/res/", lod=lod)
converter.LoadDBFolder(dbfolderlocation = "app/luclient/res/") converter.LoadDBFolder(dbfolderlocation="app/luclient/res/")
converter.LoadScene(filename=lxf_filename) converter.LoadScene(filename=lxf_filename)
converter.Export(filename=obj_filename) converter.Export(filename=obj_filename)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,11 +1,13 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, request, current_app from flask import render_template, Blueprint, current_app
from flask_user import login_required, current_user from flask_user import login_required
from app.models import db, CharacterInfo, Account, CharacterXML, Reports from app.models import CharacterInfo, Account, CharacterXML, Reports
from app import gm_level, scheduler from app import gm_level, scheduler
import datetime, xmltodict, json import datetime
import xmltodict
reports_blueprint = Blueprint('reports', __name__) reports_blueprint = Blueprint('reports', __name__)
@reports_blueprint.route('/', methods=['GET', 'POST']) @reports_blueprint.route('/', methods=['GET', 'POST'])
@login_required @login_required
@gm_level(3) @gm_level(3)
@ -13,25 +15,28 @@ def index():
reports = Reports.query.distinct(Reports.date).group_by(Reports.date).all() reports = Reports.query.distinct(Reports.date).group_by(Reports.date).all()
return render_template('reports/index.html.j2', reports=reports) return render_template('reports/index.html.j2', reports=reports)
@reports_blueprint.route('/items/by_date/<date>', methods=['GET', 'POST']) @reports_blueprint.route('/items/by_date/<date>', methods=['GET', 'POST'])
@login_required @login_required
@gm_level(3) @gm_level(3)
def items_by_date(date): 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) return render_template('reports/items/by_date.html.j2', data=data, date=date)
@reports_blueprint.route('/currency/by_date/<date>', methods=['GET', 'POST']) @reports_blueprint.route('/currency/by_date/<date>', methods=['GET', 'POST'])
@login_required @login_required
@gm_level(3) @gm_level(3)
def currency_by_date(date): 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) return render_template('reports/currency/by_date.html.j2', data=data, date=date)
@reports_blueprint.route('/uscore/by_date/<date>', methods=['GET', 'POST']) @reports_blueprint.route('/uscore/by_date/<date>', methods=['GET', 'POST'])
@login_required @login_required
@gm_level(3) @gm_level(3)
def uscore_by_date(date): 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) 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") current_app.logger.info("Start Item Report Generation")
date = datetime.date.today().strftime('%Y-%m-%d') 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 # Only one report per day
if report != None: if report not None:
current_app.logger.info(f"Item Report Already Generated for {date}") current_app.logger.info(f"Item Report Already Generated for {date}")
return return
char_xmls = CharacterXML.query.join( char_xmls = CharacterXML.query.join(
CharacterInfo, CharacterInfo,
CharacterInfo.id==CharacterXML.id CharacterInfo.id == CharacterXML.id
).join( ).join(
Account, Account,
CharacterInfo.account_id==Account.id CharacterInfo.account_id == Account.id
).filter(Account.gm_level < 3).all() ).filter(Account.gm_level < 3).all()
report_data={} report_data = {}
for char_xml in char_xmls: for char_xml in char_xmls:
try: try:
@ -66,7 +71,7 @@ def gen_item_report():
attr_prefix="attr_" attr_prefix="attr_"
) )
for inv in character_json["obj"]["inv"]["items"]["in"]: 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"]: for item in inv["i"]:
if item["attr_l"] in report_data: if item["attr_l"] in report_data:
report_data[item["attr_l"]] = report_data[item["attr_l"]] + int(item["attr_c"]) 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") current_app.logger.info("Start Currency Report Generation")
date = datetime.date.today().strftime('%Y-%m-%d') 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 # Only one report per day
if report != None: if report not None:
current_app.logger.info(f"Currency Report Already Generated for {date}") current_app.logger.info(f"Currency Report Already Generated for {date}")
return return
characters = CharacterXML.query.join( characters = CharacterXML.query.join(
CharacterInfo, CharacterInfo,
CharacterInfo.id==CharacterXML.id CharacterInfo.id == CharacterXML.id
).join( ).join(
Account, Account,
CharacterInfo.account_id==Account.id CharacterInfo.account_id == Account.id
).filter(Account.gm_level < 3).all() ).filter(Account.gm_level < 3).all()
report_data={} report_data = {}
for character in characters: for character in characters:
try: try:
@ -119,7 +124,7 @@ def gen_currency_report():
character.xml_data, character.xml_data,
attr_prefix="attr_" 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: except Exception as e:
current_app.logger.error(f"REPORT::CURRENCY - ERROR PARSING CHARACTER {char_xml.id}") current_app.logger.error(f"REPORT::CURRENCY - ERROR PARSING CHARACTER {char_xml.id}")
current_app.logger.error(f"REPORT::CURRENCY - {e}") 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") current_app.logger.info("Start U-Score Report Generation")
date = datetime.date.today().strftime('%Y-%m-%d') 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 # Only one report per day
if report != None: if report not None:
current_app.logger.info(f"U-Score Report Already Generated for {date}") current_app.logger.info(f"U-Score Report Already Generated for {date}")
return return
characters = CharacterXML.query.join( characters = CharacterXML.query.join(
CharacterInfo, CharacterInfo,
CharacterInfo.id==CharacterXML.id CharacterInfo.id == CharacterXML.id
).join( ).join(
Account, Account,
CharacterInfo.account_id==Account.id CharacterInfo.account_id == Account.id
).filter(Account.gm_level < 3).all() ).filter(Account.gm_level < 3).all()
report_data={} report_data = {}
for character in characters: for character in characters:
try: try:
@ -167,7 +172,7 @@ def gen_uscore_report():
character.xml_data, character.xml_data,
attr_prefix="attr_" 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: 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 - ERROR PARSING CHARACTER {char_xml.id}")
current_app.logger.error(f"REPORT::U-SCORE - {e}") current_app.logger.error(f"REPORT::U-SCORE - {e}")

View File

@ -1,7 +1,21 @@
from flask_marshmallow import Marshmallow 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() ma = Marshmallow()
class PlayKeySchema(ma.SQLAlchemyAutoSchema): class PlayKeySchema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = PlayKey model = PlayKey
@ -44,7 +58,6 @@ class PropertyContentSchema(ma.SQLAlchemyAutoSchema):
ugc = ma.Nested(UGCSchema) ugc = ma.Nested(UGCSchema)
class PropertySchema(ma.SQLAlchemyAutoSchema): class PropertySchema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Property model = Property

6
pylama.ini Normal file
View 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