diff --git a/v8_server/eamuse/services/__init__.py b/v8_server/eamuse/services/__init__.py
index e69de29..ec86f9d 100644
--- a/v8_server/eamuse/services/__init__.py
+++ b/v8_server/eamuse/services/__init__.py
@@ -0,0 +1,18 @@
+from v8_server.eamuse.services.facility import Facility
+from v8_server.eamuse.services.message import Message
+from v8_server.eamuse.services.package import Package
+from v8_server.eamuse.services.pcbevent import PCBEvent
+from v8_server.eamuse.services.pcbtracker import PCBTracker
+from v8_server.eamuse.services.services import ServiceRequest, Services, ServiceType
+
+
+__all__ = [
+ "Facility",
+ "Message",
+ "PCBEvent",
+ "PCBTracker",
+ "Package",
+ "ServiceRequest",
+ "ServiceType",
+ "Services",
+]
diff --git a/v8_server/eamuse/services/facility.py b/v8_server/eamuse/services/facility.py
new file mode 100644
index 0000000..38fd146
--- /dev/null
+++ b/v8_server/eamuse/services/facility.py
@@ -0,0 +1,113 @@
+from lxml.builder import E
+
+from v8_server.eamuse.utils.xml import XMLBinTypes as T, e_type
+
+
+class Facility(object):
+ """
+ Handle the Facility request.
+
+ The only method of note is the "get" method. This method expects to return a bunch
+ of information about the arcade this cabinet is in, as well as some settings for
+ URLs and the name of the cab.
+
+ Example:
+
+
+
+ """
+
+ # Methods
+ GET = "get"
+
+ @classmethod
+ def get(cls):
+ """
+ Example:
+
+
+
+ US-123
+ US
+ .
+ H
+ 0
+
+
+ .
+ 0
+
+
+ 127.0.0.1
+ 80
+ 80
+
+
+ 1
+ .
+ 0
+ 0
+
+
+
+ 3000
+ 3
+ 10000
+
+
+ 365
+
+
+ www.ea-pass.konami.net
+ www.konami.jp/am
+ http://am.573.jp
+ http://id.konami.net
+ http://eagate.573.jp
+
+
+
+
+ """
+
+ # TODO: The facility data should be read in from a config file instead of being
+ # hard coded here
+
+ return E.response(
+ E.facility(
+ E.location(
+ E.id("CA-123"),
+ E.country("CA"),
+ E.region("MB"),
+ E.name("SenPi Arcade"),
+ E.type("0", e_type(T.u8)),
+ ),
+ E.line(E.id("."), E("class", "0", e_type(T.u8))),
+ E.portfw(
+ E.globalip("127.0.0.1", e_type(T.ip4, count=1)),
+ E.globalport("80", e_type(T.u16)),
+ E.privateport("80", e_type(T.u16)),
+ ),
+ E.public(
+ E.flag("1", e_type(T.u8)),
+ E.name("Gotem"),
+ E.latitude("0"),
+ E.longitude("0"),
+ ),
+ E.share(
+ E.eacoin(
+ E.notchamount("3000", e_type(T.s32)),
+ E.notchcount("3", e_type(T.s32)),
+ E.supplylimit("10000", e_type(T.s32)),
+ ),
+ E.eapass(E.valid("365", e_type(T.u16))),
+ E.url(
+ E.eapass("www.ea-pass.konami.net"),
+ E.arcadefan("www.konami.jp/am"),
+ E.konaminetdx("http://am.573.jp"),
+ E.konamiid("http://id.konami.net"),
+ E.eagate("http://eagate.573.jp"),
+ ),
+ ),
+ {"expire": "600"},
+ )
+ )
diff --git a/v8_server/eamuse/services/message.py b/v8_server/eamuse/services/message.py
new file mode 100644
index 0000000..503005d
--- /dev/null
+++ b/v8_server/eamuse/services/message.py
@@ -0,0 +1,27 @@
+from lxml.builder import E
+
+
+class Message(object):
+ """
+ Handle the Message request.
+
+ It is unknown as to what this does. Possibly it's for operator messages
+
+ Example:
+
+
+
+ """
+
+ # Methods
+ GET = "get"
+
+ @classmethod
+ def get(cls):
+ """
+ Example:
+
+
+
+ """
+ return E.response(E.message({"expire": "600"}))
diff --git a/v8_server/eamuse/services/package.py b/v8_server/eamuse/services/package.py
new file mode 100644
index 0000000..8740ae1
--- /dev/null
+++ b/v8_server/eamuse/services/package.py
@@ -0,0 +1,44 @@
+from lxml.builder import E
+
+from v8_server.eamuse.services.services import ServiceRequest
+from v8_server.eamuse.utils.xml import get_xml_attrib
+
+
+class Package(object):
+ """
+ Handle the Package request.
+
+ This is for supporting downloading of updates. We do not support this.
+
+ Example:
+
+
+
+ """
+
+ # Methods
+ LIST = "list"
+
+ # PKGTypes
+ PKGTYPE_ALL = "all"
+
+ @classmethod
+ def list(cls, req: ServiceRequest):
+ """
+ Example:
+
+
+
+ """
+ # Grab the pkgtype in case we ever need it
+ pkgtype = get_xml_attrib(req.xml[0], "pkgtype")
+
+ if pkgtype == cls.PKGTYPE_ALL:
+ response = E.response(E.package({"expire": "600"}))
+ else:
+ raise Exception(
+ "Not sure how to handle this package request. "
+ f'pkgtype "{pkgtype}" is unknown for request: {req}'
+ )
+
+ return response
diff --git a/v8_server/eamuse/services/pcbevent.py b/v8_server/eamuse/services/pcbevent.py
new file mode 100644
index 0000000..6736135
--- /dev/null
+++ b/v8_server/eamuse/services/pcbevent.py
@@ -0,0 +1,96 @@
+from datetime import datetime
+
+from lxml import etree
+from lxml.builder import E
+
+from v8_server.eamuse.services.services import ServiceRequest
+
+
+class PCBEventItem(object):
+ """
+ Contains the data for a PCBEvent Item
+
+ Example:
+ -
+ K32.mode.std
+ 1
+
+
+ """
+
+ DATE_FMT = "%b/%d/%Y-%H:%M:%S"
+
+ def __init__(self, xml_root: etree) -> None:
+ # Build our data from the `item` xml root object
+ self.name = xml_root.find("name").text
+ self.value = int(xml_root.find("value").text)
+ self.time = datetime.fromtimestamp(int(xml_root.find("time").text))
+
+ def __repr__(self) -> str:
+ return (
+ f'PCBEventItem"
+ )
+
+
+class PCBEvent(object):
+ """
+ Handle the PCBEvent request.
+
+ It is unknown as to what this does.
+
+ Example:
+
+
+
+ 4
+ -
+ K32.mode.std
+ 1
+
+
+ -
+ K32.playcnt.t
+ 1
+
+
+ -
+ game.e
+ 1
+
+
+
+
+ """
+
+ DATE_FMT = "%b/%d/%Y-%H:%M:%S"
+
+ def __init__(self, req: ServiceRequest) -> None:
+ # Build our item from the data
+ xml_root = req.xml[0]
+
+ self.time = datetime.fromtimestamp(int(xml_root.find("time").text))
+ self.seq = int(xml_root.find("seq").text)
+
+ self.items = []
+ for item in xml_root.findall("item"):
+ self.items.append(PCBEventItem(item))
+
+ # Methods
+ PUT = "put"
+
+ @classmethod
+ def put(cls):
+ """
+ Example:
+
+
+
+ """
+ return E.response(E.pcbevent({"expire": "600"}))
+
+ def __repr__(self) -> str:
+ return (
+ f"PCBEvent"
+ )
diff --git a/v8_server/eamuse/services/pcbtracker.py b/v8_server/eamuse/services/pcbtracker.py
index 9d8603e..8b1d454 100644
--- a/v8_server/eamuse/services/pcbtracker.py
+++ b/v8_server/eamuse/services/pcbtracker.py
@@ -22,4 +22,17 @@ class PCBTracker(object):
@classmethod
def alive(cls):
+ """
+ Example (if Paseli is not active):
+
+
+
+
+ Potentially if Paseli is active, the response might look like so:
+
+
+
+
+ I am unsure what the `time`, `limit`, and `eclimit` responses would be.
+ """
return E.response(E.pcbtracker({"ecenable": "1" if cls.PASELI_ACTIVE else "0"}))
diff --git a/v8_server/eamuse/services/services.py b/v8_server/eamuse/services/services.py
index b3e0854..6a8e36c 100644
--- a/v8_server/eamuse/services/services.py
+++ b/v8_server/eamuse/services/services.py
@@ -8,13 +8,13 @@ from random import randint
from time import time
from typing import Any, Callable, Dict, Optional, Tuple, Union
-from flask import Flask, Request
+from flask import Request
from kbinxml import KBinXML
from lxml import etree
from lxml.builder import E
from lxml.etree import _Element as eElement
-from v8_server import LOG_PATH
+from v8_server import LOG_PATH, app
from v8_server.eamuse.utils.arc4 import EAmuseARC4
from v8_server.eamuse.utils.eamuse import Model
from v8_server.eamuse.utils.lz77 import Lz77
@@ -101,7 +101,7 @@ class Services(object):
return services
@classmethod
- def route(cls, app: Flask, _type: ServiceType) -> Callable[[Any], Any]:
+ def route(cls, _type: ServiceType) -> Callable[[Any], Any]:
def decorator(f):
func = app.route(f"/{_type.value}", methods=["POST"])(f)
return func
diff --git a/v8_server/eamuse/utils/eamuse.py b/v8_server/eamuse/utils/eamuse.py
index 6a8df62..9bf737b 100644
--- a/v8_server/eamuse/utils/eamuse.py
+++ b/v8_server/eamuse/utils/eamuse.py
@@ -1,26 +1,6 @@
from __future__ import annotations
-from typing import Dict, Optional
-
-
-class XMLBinTypes(object):
- s8 = "s8"
- u8 = "u8"
- s16 = "s16"
- u16 = "u16"
- s32 = "s32"
- u32 = "u32"
- s64 = "s64"
- u64 = "u64"
- ip4 = "ip4"
- time = "time"
-
-
-def e_type(_type, count: Optional[int] = None) -> Dict[str, str]:
- result = {"__type": _type}
- if count is not None:
- result["__count"] = str(count)
- return result
+from typing import Optional
class Model:
diff --git a/v8_server/eamuse/utils/xml.py b/v8_server/eamuse/utils/xml.py
index f39b7c2..99bca84 100644
--- a/v8_server/eamuse/utils/xml.py
+++ b/v8_server/eamuse/utils/xml.py
@@ -1,9 +1,29 @@
from datetime import datetime
-from typing import Optional
+from typing import Dict, Optional
from lxml import etree
+class XMLBinTypes(object):
+ s8 = "s8"
+ u8 = "u8"
+ s16 = "s16"
+ u16 = "u16"
+ s32 = "s32"
+ u32 = "u32"
+ s64 = "s64"
+ u64 = "u64"
+ ip4 = "ip4"
+ time = "time"
+
+
+def e_type(_type, count: Optional[int] = None) -> Dict[str, str]:
+ result = {"__type": _type}
+ if count is not None:
+ result["__count"] = str(count)
+ return result
+
+
def get_xml_tag(xml: etree) -> str:
return str(xml.tag)
diff --git a/v8_server/view/index.py b/v8_server/view/index.py
index a021010..1a7c345 100644
--- a/v8_server/view/index.py
+++ b/v8_server/view/index.py
@@ -2,14 +2,19 @@ import random
from typing import Dict, Tuple
from flask import request
-from lxml import etree as ET # noqa: N812
-from lxml.builder import E
from sqlalchemy.orm.exc import MultipleResultsFound
from v8_server import app
-from v8_server.eamuse.services.pcbtracker import PCBTracker
-from v8_server.eamuse.services.services import ServiceRequest, Services, ServiceType
-from v8_server.eamuse.utils.eamuse import e_type
+from v8_server.eamuse.services import (
+ Facility,
+ Message,
+ Package,
+ PCBEvent,
+ PCBTracker,
+ ServiceRequest,
+ Services,
+ ServiceType,
+)
from v8_server.eamuse.utils.xml import get_xml_attrib
from v8_server.model.connection import Database
from v8_server.model.user import Card, ExtID, Profile, RefID, User
@@ -43,13 +48,7 @@ def catch_all(u_path: str) -> str:
return "You want path: %s" % u_path
-def base_response(element: str, attributes: Dict[str, str] = None) -> ET:
- if attributes is None:
- attributes = {}
- return E.response(E(element, {**attributes, "expire": "600"}))
-
-
-@Services.route(app, ServiceType.PCBTRACKER)
+@Services.route(ServiceType.PCBTRACKER)
def pcbtracker_service() -> FlaskResponse:
req = ServiceRequest(request)
@@ -61,89 +60,57 @@ def pcbtracker_service() -> FlaskResponse:
return req.response(response)
+@Services.route(ServiceType.MESSAGE)
+def message_service() -> FlaskResponse:
+ req = ServiceRequest(request)
+
+ if req.method == Message.GET:
+ response = Message.get()
+ else:
+ raise Exception(f"Not sure how to handle this Message Request: {req}")
+
+ return req.response(response)
+
+
+@Services.route(ServiceType.PCBEVENT)
+def pcbevent_service() -> FlaskResponse:
+ req = ServiceRequest(request)
+ event = PCBEvent(req)
+ app.logger.info(event)
+
+ if req.method == PCBEvent.PUT:
+ response = PCBEvent.put()
+ else:
+ raise Exception(f"Not sure how to handle this PCBEvent Request: {req}")
+
+ return req.response(response)
+
+
+@Services.route(ServiceType.PACKAGE)
+def package_service() -> FlaskResponse:
+ req = ServiceRequest(request)
+
+ if req.method == Package.LIST:
+ response = Package.list(req)
+ else:
+ raise Exception(f"Not sure how to handle this Package Request: {req}")
+
+ return req.response(response)
+
+
+@Services.route(ServiceType.FACILITY)
+def facility_service() -> FlaskResponse:
+ req = ServiceRequest(request)
+
+ if req.method == Facility.GET:
+ response = Facility.get()
+ else:
+ raise Exception(f"Not sure how to handle this Facility Request: {req}")
+
+ return req.response(response)
+
+
'''
-@app.route("/message/service", methods=["POST"])
-def message() -> Tuple[bytes, Dict[str, str]]:
- """
- Unknown what this does. Possibly for operator messages?
- """
- _ = eamuse_read_xml(request)
- response = base_response("message")
- return eamuse_prepare_xml(response, request)
-
-
-@app.route("/pcbevent/service", methods=["POST"])
-def pcbevent() -> Tuple[bytes, Dict[str, str]]:
- """
- Handle a PCBEvent request. We do nothing for this aside from logging the event.
- """
- _ = eamuse_read_xml(request)
- response = base_response("pcbevent")
- return eamuse_prepare_xml(response, request)
-
-
-@app.route("/facility/service", methods=["POST"])
-def facility() -> Tuple[bytes, Dict[str, str]]:
- """
- Handle a facility request. The only method of note is the "get" request,
- which expects to return a bunch of information about the arcade this cabinet is in,
- as well as some settings for URLs and the name of the cab.
- """
- _ = eamuse_read_xml(request)
-
- response = E.response(
- E.facility(
- E.location(
- E.id("US-123"),
- E.country("US"),
- E.region("."),
- E.name("H"),
- E.type("0", e_type("u8")),
- ),
- E.line(E.id("."), E("class", "0", e_type("u8"))),
- E.portfw(
- E.globalip("192.168.1.139", e_type("ip4", count=1)),
- E.globalport("80", e_type("u16")),
- E.privateport("80", e_type("u16")),
- ),
- E.public(
- E.flag("1", e_type("u8")),
- E.name("."),
- E.latitude("0"),
- E.longitude("0"),
- ),
- E.share(
- E.eacoin(
- E.notchamount("3000", e_type("s32")),
- E.notchcount("3", e_type("s32")),
- E.supplylimit("10000", e_type("s32")),
- ),
- E.eapass(E.valid("365", e_type("u16"))),
- E.url(
- E.eapass("www.ea-pass.konami.net"),
- E.arcadefan("www.konami.jp/am"),
- E.konaminetdx("http://am.573.jp"),
- E.konamiid("http://id.konami.net"),
- E.eagate("http://eagate.573.jp"),
- ),
- ),
- {"expire": "600"},
- )
- )
- return eamuse_prepare_xml(response, request)
-
-
-@app.route("/package/service", methods=["POST"])
-def package() -> Tuple[bytes, Dict[str, str]]:
- """
- This is for supporting downloading of updates. We don't support this.
- """
- _ = eamuse_read_xml(request)
-
- response = base_response("package")
- return eamuse_prepare_xml(response, request)
-
-
class CardStatus(object):
"""
List of statuses we return to the game for various reasons