Advertisement
Guest User

Untitled

a guest
Jun 24th, 2017
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 19.17 KB | None | 0 0
  1. # x is rank x/x with x points. x/x with x% (x/x) tricks completed
  2. # x completed x in x.6d seconds going x mph!
  3.  
  4. import es
  5. import os
  6. import time
  7. import psyco
  8. import cfglib
  9. import sqlite3
  10. import vecmath
  11. import usermsg
  12. import langlib
  13. import popuplib
  14. import effectlib
  15. import playerlib
  16. import gamethread
  17. import collections
  18.  
  19.  
  20.  
  21. class SQLiteManager(object):
  22.     """
  23.     This class manages all database connections. The main idea of this object
  24.     is to create an easy interface so we can easilly and safely query values
  25.     from a database witout worrying about convulent and complex sqlite syntax.
  26.    
  27.     This interface allows us to safely call and remove objects from the database
  28.     so that users do not have to access the database directly, but through
  29.     a simple API structure.
  30.    
  31.     We should only have one database instance so it is unnecesarry to create
  32.     another object to hold the instances of this object.
  33.     """
  34.     players = []
  35.     def __init__(self, path):
  36.         """
  37.         Default Constructor
  38.        
  39.         Initialize all the default values and open a connection with the SQLite
  40.         database. Create the tables if they don't exist.
  41.        
  42.         @PARAM path - the absolute path of the database
  43.         """
  44.         self.path       = path
  45.         self.connection = sqlite3.connect(path)
  46.         self.connection.text_factory = str
  47.         self.cursor     = self.connection.cursor()
  48.        
  49.         self.cursor.execute('PRAGMA journal_mode=OFF')
  50.         self.cursor.execute('PRAGMA locking_mode=EXCLUSIVE')
  51.         self.cursor.execute('PRAGMA synchronous=OFF')
  52.        
  53.         self.cursor.execute("""\CREATE TABLE IF NOT EXISTS Player (...)""")
  54.        
  55.     def __del__(self):
  56.         """
  57.         Default deconstructor.
  58.        
  59.         Executed when a database instance is destroyed. Ensure that the database
  60.         is saved and closed.
  61.         """
  62.         self.save()
  63.         self.close()
  64.        
  65.     def __contains__(self, key):
  66.         """
  67.         Executed automatically when we attempt to test to see if an element exists
  68.         within the database.
  69.        
  70.         @PARAM key - The value to test for validity
  71.         @RETURN boolean - Whether or not the value exists
  72.         """
  73.         key = str(key)
  74.         if key in self.players:
  75.             return True
  76.         self.execute('SELECT UserID FROM Player WHERE steamid=?', key)
  77.         result = self.cursor.fetchone()
  78.         if bool(result):
  79.             self.players.append(key)
  80.             return True
  81.         return False
  82.        
  83.     def __iter__(self):
  84.         """
  85.         Executed automatically when we attempt to iterate through an instance
  86.         of this class. Query all the steamids from the playerstats table and
  87.         yield each steamid as a seperate object.
  88.        
  89.         @RETURN yield object - string objects which represent player's steamids
  90.         """
  91.         self.execute('SELECT ... FROM Player')
  92.         self.cursor.fetchall().itervalues()
  93.        
  94.     def execute(self, parseString, *args):
  95.         """
  96.         A wrapper function to simulate the execute() method of a cursor object.
  97.        
  98.         @PARAM parseString - the string query line of a SQLite statement
  99.         """
  100.         self.cursor.execute(parseString, args)
  101.        
  102.     def addPlayer(self, steamid, name):
  103.         """
  104.         Add a player to the database and ensure that they exist within the
  105.         playerstats table as well as the playerskills table.
  106.        
  107.         @PARAM steamid - the string value of a players steamid
  108.         @PARAM name - the string value of a players name
  109.         """
  110.         self.execute('INSERT INTO Player (...) VALUES (...)',)
  111.         return self.cursor.lastrowid
  112.            
  113.     def getUserIdFromSteamId(self, steamId):
  114.         """
  115.         Return the UserID auto increment value from a given steamid
  116.        
  117.         @PARAM steamId - the steamid of the player
  118.         @RETURN INTEGER - the row, if the user doesn't exist, then return None
  119.         """
  120.         self.execute('SELECT UserID FROM Player WHERE steamid=?', steamId)
  121.         value = self.cursor.fetchone()
  122.         if value is None:
  123.             return None
  124.         return value[0]
  125.        
  126.     def getPlayerInfo(self, userid, infoType):
  127.         """
  128.         Returns a players attribute from the playerstats table.
  129.        
  130.         @PARAM userid   - the integer userid of the player your wish to check
  131.         @PARAM infoType - the column name of the value you wish to return
  132.         @RETURN object  - returns an object type of the value to which statType returns
  133.         """
  134.         if not isinstance(userid, int):
  135.             userid = self.getUserIdFromSteamId(userid)
  136.         infoType = str(infoType).replace("'", "''")
  137.         if hasattr(infoType, "__len__"):
  138.             query = "SELECT " + ",".join(map(str, infoType)) + " FROM Player WHERE UserID=?"
  139.         else:
  140.             query = "SELECT " + str(infoType) + " FROM Player WHERE UserID=?"
  141.         self.execute(query, userid)
  142.         return self.fetchone()
  143.  
  144.     def update(self, table, primaryKeyName, primaryKeyValue, options):
  145.         """
  146.         Sets certain values to a new amount referenced by a table, primary key
  147.         and primary key value
  148.        
  149.         @PARAM table - the table of which this should take place
  150.         @PARAM primaryKeyName - the name of the key you wish to test for equality
  151.         @PARAM primaryKeyValue - the options to update where the primaryKeyName's value equates to this value
  152.         @PARAM option - dictionary of values, the key is the row to update, the value is the new amount to set the value to
  153.         """
  154.         keys = ""
  155.         if not isinstance(options, dict):
  156.             raise ValueError, "Expected 'options' argument to be a dictionary, instead received: %s" % type(options).__name__
  157.         if options:
  158.             for key, value in options.iteritems():
  159.                 if isinstance(key, str):
  160.                     key   = key.replace("'", "''")
  161.  
  162.                 if isinstance(value, str):
  163.                     value = value.replace("'", "''")
  164.                 keys += "%s='%s'," % (key, value)
  165.             keys = keys[:-1]
  166.             query = "UPDATE " + str(table) + " SET " + keys + " WHERE " + str(primaryKeyName) + "='" + str(primaryKeyValue) + "'"
  167.             self.execute(query)
  168.        
  169.        
  170.     def increment(self, table, primaryKeyName, primaryKeyValue, options):
  171.         """
  172.         Increments certain values by a positive amount referenced by a
  173.         table, primary key and primary key value
  174.        
  175.         @PARAM table - the table of which this increment should take place
  176.         @PARAM primaryKeyName - the name of the key you wish to test for equality
  177.         @PARAM primaryKeyValue - the options to update where the primaryKeyName's value equates to this value
  178.         @PARAM option - dictionary of values, the key is the row to update, the value is the amount to increment the current value by
  179.         """
  180.         keys = ''
  181.         if not isinstance(options, dict):
  182.             raise ValueError, "Expected 'options' argument to be a dictionary, instead received: %s" % type(options).__name__
  183.         for key, value in options.iteritems():
  184.             if isinstance(key, str):
  185.                 key   = key.replace("'", "''")
  186.             if isinstance(value, str):
  187.                 value = value.replace("'", "''")
  188.             keys += "%s=%s+%i," % (key, key, value)
  189.         keys = keys[:-1]
  190.         self.execute('UPDATE ? SET %s WHERE ?=?+?' % keys, table, primaryKeyName, primaryKeyName, primaryKeyValue)
  191.        
  192.     def query(self, table, primaryKeyName, primaryKeyValue, options):
  193.         """
  194.         Queries results from the table for one person and returns either a
  195.         tuple or a single value depending on the amount of values passed.
  196.        
  197.         @PARAM table - the table of which this query should take place
  198.         @PARAM primaryKeyName - the name of the key you wish to test for equality
  199.         @PARAM primaryKeyValue - the options to update where the primaryKeyName's value equates to this value
  200.         @PARAM options - either a single value or a tuple which we will get the values of
  201.         """
  202.         if hasattr(options, '__len__'):
  203.             query = "SELECT " + ",".join(map(lambda x: str(x).replace("'", "''"), options)) + " FROM " + table \
  204.                     + " WHERE " + primaryKeyName + "='" + primaryKeyValue + "'"
  205.         else:
  206.             query = "SELECT " + str(options).replace("'", "''") + " FROM " + table + \
  207.                     " WHERE " + primaryKeyName + "='" + primaryKeyValue + "'"
  208.         self.execute(query)
  209.         return self.fetchone()
  210.        
  211.     def fetchall(self):
  212.         """
  213.         Mimics the sqlite fetchall method which recides within a cursor object.
  214.         Ensures that the true values are added to a list so that we don't need
  215.         to index it if the value is only one item in length (e.g. item instead
  216.         of (item,)...)
  217.        
  218.         @RETURN list - attributes from which the query returned
  219.         """
  220.         trueValues = []
  221.         for value in self.cursor.fetchall():
  222.             if isinstance(value, tuple):
  223.                 if len(value) > 1:
  224.                     tempValues = []
  225.                     for tempValue in value:
  226.                         if isinstance(tempValue, long):
  227.                             tempValue = int(tempValue)
  228.                         tempValues.append(tempValue)
  229.                     trueValues.append(tempValues)
  230.                 else:
  231.                     if isinstance(value[0], long):
  232.                         trueValues.append(int(value[0]))
  233.                     else:
  234.                         trueValues.append(value[0])
  235.             else:
  236.                 if isinstance(value, long):
  237.                     value = int(value)
  238.                 trueValues.append(value)
  239.         return trueValues
  240.        
  241.     def fetchone(self):
  242.         """
  243.         Mimics the sqlite fetchone method which recides within a cursor object.
  244.         Ensures that a single value is returned from the cursor object if only
  245.         one object exists within the tuple from the query, otherwise it returns
  246.         the query result
  247.        
  248.         @RETURN Object - the result from the query command
  249.         """
  250.         result = self.cursor.fetchone()
  251.         if hasattr(result, '__iter__'):
  252.             if len(result) == 1:
  253.                 trueResults = result[0]
  254.                 if isinstance(trueResults, long):
  255.                     trueResults = int(trueResults)
  256.                 return trueResults
  257.             else:
  258.                 trueResults = []
  259.                 for trueResult in result:
  260.                     if isinstance(trueResult, long):
  261.                         trueResult = int(trueResult)
  262.                     trueResults.append(trueResult)
  263.                 return trueResults
  264.         if isinstance(result, long):
  265.             result = int(result)
  266.         return result  
  267.        
  268.     def save(self):
  269.         """
  270.         Commits the database to harddrive so that we can load it if the server
  271.         closes.
  272.         """
  273.         if self.path != ':memory:':
  274.             self.connection.commit()
  275.        
  276.     def close(self):
  277.         """
  278.         Closes the connections so that no further queries can be made.
  279.         """
  280.         self.cursor.close()
  281.         self.connection.close()
  282.    
  283. class PlayerManager(object):
  284.     """
  285.     This class is used to manage players. This class itself simulates a
  286.     dictionary but gives additional functions which allow us to modify the way
  287.     the singelton is perceived. This will store individual Player objects by a
  288.     tag name and allows us to reference the singleton object by dictionary item
  289.     assignment / retrieval to retrieve the retrespective player object.
  290.     """
  291.     def __init__(self):
  292.         """ Default constructor, initialize variables """
  293.         self.players = {}
  294.        
  295.     def __getitem__(self, userid):
  296.         """
  297.         Executed when object[x] is executed on the singleton. As we wish to
  298.         simulate dictionary attributes, we want to return the player object
  299.         referenced by their userid
  300.        
  301.         @PARAM int|str userid - The ID of the user we wish to return the Player object
  302.         @RETURN Player - The Player object referenced by ID
  303.         """
  304.         return self.players[userid]
  305.        
  306.     def __iter__(self):
  307.         """
  308.         This function executes automatically when "for x in object" is executed.
  309.         We want to return all the player objects inside the player dictionary.
  310.        
  311.         @RETURN generator -  A generator to a list of all the player objects
  312.         """
  313.         return self.players.itervalues()
  314.  
  315.     def __contains__(self, userid):
  316.         """
  317.         Executed automatically when we use the syntax "x in object". We will
  318.         test if the userid in question exists within the players dictionary.
  319.        
  320.         @PARAM int|str userid - The userid to test for validation
  321.         @RETURN boolean - Whether or not the userid already exists
  322.         """
  323.         return userid in self.players
  324.        
  325.     def __delitem__(self, userid):
  326.         """
  327.         Removes the player and object from memory so the RAM is cleared.
  328.         object.removePlayer(userid) == object.__delitem__(userid)
  329.        
  330.         @PARAM int|str userid - The userid of the player to remove
  331.         """
  332.         self.removePlayer(userid)
  333.        
  334.     def addPlayer(self, userid):
  335.         """
  336.         Add the player into memory as an Player object
  337.        
  338.         @PARAM int|str userid - The userid of the player to add
  339.         """
  340.         self.players[userid] = Player(userid)
  341.        
  342.     def removePlayer(self, userid):
  343.         """
  344.         Removes the player and object from memory
  345.        
  346.         @PARAM int|str userid - The userid of the player to remove
  347.         """
  348.         if userid in self.players:
  349.             del self.players[userid]
  350.    
  351. class PlayerObject(object):
  352.     """
  353.     This is an object which will revolve around all active players. This should
  354.     contain all relevant information about a particular player, and cache all
  355.     their details so we don't need to query the database at regular intervals.
  356.     """
  357.     cache  = None
  358.     userid = None
  359.     def __init__(self, userid):
  360.         """"
  361.         Default constructor, initialize variables
  362.         Set up all necessary objects and cache values here.
  363.        
  364.         @PARAM int|str userid - The ID of the user who this object references
  365.         """
  366.         self.userid = int(userid)
  367.         self.cache = PlayerCache(self.userid)
  368.        
  369.     def __getattr__(self, attribute):
  370.         """
  371.         This object will allow us to get attributes from the cache object
  372.        
  373.         @PARAM str attribute  - The attribute of the PlayerCache we want to
  374.         @RETURN str attribute - The attribute if it he exist
  375.         """
  376.         if hasattr(self.cache, attribute):
  377.             return getattr(self.cache, attribute)
  378.         raise AttributeError('Player object has no attribute %s' % attribute)
  379.        
  380.     def __setattr__(self, attribute, value):
  381.         """
  382.         This object will allow us to reassing values in the PlayerCache object
  383.        
  384.         @PARAM str attribute     - The attribute to change
  385.         @PARAM int|str value     - The value to set to the attribute
  386.         @RETURN mixed attribute  - setattr object if cache has attribute
  387.         """
  388.         if hasattr(self.cache, attribute):
  389.             return setattr(self.cache, attribute, value)
  390.         object.__setattr__(attribute, value)
  391.    
  392. class PlayerCache(object):
  393.     """
  394.     This object should be used by each player and should store all relevant
  395.     information regarding their current stats. Each time he make a trick, if the
  396.     information has not already been querieid, query it and cache it. Only
  397.     refresh if explicitly told to; we never need to refresh on ticks. This means
  398.     we can reduce the amount of DB queries we need which improves read time and
  399.     efficiency.
  400.     """
  401.     def __init__(self, userid):
  402.         """"
  403.         Default constructor, initialize variables
  404.        
  405.         @PARAM str|int userid - The ID of the user on the server
  406.         """
  407.         self.userid  = userid                      
  408.         self.steamid = playerlib.uniqueid(self.userid, True)
  409.         self.update()
  410.        
  411.     def update(self):
  412.         """
  413.         This method simply refreshes the local attributes
  414.         with the attributes from the dictionnary.
  415.         """
  416.         database.query('SELECT name, tricks FROM player WHERE steamid=?', self.steamid)
  417.         name, tricks = database.fetchone()
  418.         self.oldName = self.name = name
  419.         self.tricks  = self.oldTricks = tricks
  420.        
  421.     def commit(self):
  422.         """
  423.         This method should commit the local attributes to the database
  424.         only if the local values have been altered.
  425.         """
  426.         if self.name != self.oldName or self.tricks != self.oldTricks:
  427.             database.query('UPDATE player SET name=?, tricks=? WHERE steamid=?', (self.name, self.tricks, self.steamid))
  428.             self.oldName = self.name
  429.             self.oldTricks = self.tricks
  430.    
  431. class PopupManager(object):
  432.     ...
  433.    
  434. class MessageManager(object):
  435.     ...
  436.    
  437. class HUDHintManager(object):
  438.     """
  439.     This class will be used for HUDS management
  440.     """
  441.     def __init__(self):
  442.         """ Default constructor, initialize variables """
  443.         self._name = '%s(%s)' % (self.__class__.__name__, id(self))
  444.         self.players = {}
  445.         self._refresh()
  446.    
  447.     def stop(self):
  448.         """ Static method for stop all delays """
  449.         gamethread.cancelDelayed(self._name)
  450.         self.players.clear()
  451.    
  452.     def addLine(self, player, line, timeout=10):
  453.         """
  454.         Add a line to the HUD of a player
  455.        
  456.         @PARAM player  - UserID of the player
  457.         @PARAM line - Line to add
  458.         @PARAM timeout - The time after the line is deleted, 10 by default
  459.         """
  460.         if player not in self.players:
  461.             self.players[player] = collections.deque([], 10)
  462.         self.players[player].append(line)
  463.         gamethread.delayedname(timeout if timeout < 30 else 10, self._name, self.removeLine, (player, line))
  464.        
  465.     def removeLine(self, player, line):
  466.         """
  467.         Remove a line to the HUD of a player
  468.        
  469.         @PARAM player - UserID of the player
  470.         @PARAM line   - Line to remove
  471.         """
  472.         if line in self.players[player]:
  473.             self.players[player].remove(line)
  474.             if not len(self.players[player]):
  475.                 del self.players[player]
  476.        
  477.     def _refresh(self):
  478.         """ Class method only, used to auto-refresh the HUDs """
  479.         for player in self.players:
  480.             if len(self.players[player]):
  481.                 usermsg.hudhint(player, '\n'.join(self.players[player])
  482.         gamethread.delayedname(1, self._name, self._refresh)
  483.    
  484. class Trigger(object):
  485.     """
  486.     This is an object which will revolve around all triggers. This should
  487.     contain all relevant information about a particular trigger
  488.     """
  489.     trigger = None
  490.     def __init__(self, trigger, coords):
  491.         self.trigger = trigger
  492.         self.coords  = coords
  493.    
  494. class TriggerManager(object):
  495.     """
  496.     This class is used to manage triggers.
  497.     This will store individual Trigger objects by a tag name
  498.     and allows us to reference the singleton object
  499.     """
  500.     def __init__(self, path):
  501.         """
  502.         Default constructor, initialize variables
  503.        
  504.         @PARAM str path - Path of the file who contains triggers
  505.         """
  506.         self.triggers  = {}
  507.         self.triggersC = cfglib.AddonINI(path)
  508.        
  509.     def __getitem__(self, trigger):
  510.         """
  511.         Executed when object[x] is executed on the singleton.
  512.        
  513.         @PARAM str trigger - The trigger name we wish to return the Trigger object
  514.         @RETURN Trigger    - The Trigger object referenced by name
  515.         """
  516.         return self.triggers[trigger]
  517.        
  518.     def __iter__(self):
  519.         """
  520.         This function executes automatically when "for x in object" is executed.
  521.        
  522.         @RETURN generator -  A generator to a list of all the player objects
  523.         """
  524.         return self.triggers.itervalues()
  525.  
  526.     def __contains__(self, trigger):
  527.         """
  528.         Executed automatically when we use the syntax "x in object".
  529.        
  530.         @PARAM str trigger - The trigger to test for validation
  531.         @RETURN boolean - Whether or not the trigger already exists
  532.         """
  533.         return trigger in self.triggers
  534.        
  535.     def __delitem__(self, userid):
  536.         """
  537.         Removes the trigger and object from memory so the RAM is cleared.
  538.         object.removeTrigger(userid) == object.__delitem__(userid)
  539.        
  540.         @PARAM str trigger - The trigger to remove
  541.         """
  542.         self.removeTrigger(trigger)
  543.        
  544.     def addTrigger(self, trigger, *coords):
  545.         """
  546.         Add the trigger into memory as an Trigger object
  547.        
  548.         @PARAM str trigger - The trigger to add
  549.         @PARAM list coords - List of points of the trigger
  550.         """
  551.         self.triggers[trigger] = Trigger(trigger, coords[0])
  552.         if not triggersC.has_key(trigger):
  553.             self.triggersC.addGroup(trigger)
  554.         for x in xrange(len(coords[0])):
  555.             self.triggersC.addValueToGroup(trigger, x, coords[0][x], True)
  556.         self.triggersC.write()
  557.        
  558.     def removeTrigger(self, trigger):
  559.         """
  560.         Removes the trigger and object from memory
  561.        
  562.         @PARAM str userid - The trigger to remove
  563.         """
  564.         if trigger in self.triggers:
  565.             del self.triggers[trigger]
  566.             del self.triggersC[trigger]
  567.         self.triggersC.write()
  568.    
  569. class Trick(object):
  570.     ...
  571.    
  572. class TrickManager(object):
  573.     ...
  574.  
  575. database = SQLiteManager(databasePath)
  576. players  = PlayerManager()
  577. popups   = PopupManager()
  578. messages = MessageManager()
  579. huds     = HUDHintManager()
  580. triggers = TriggerManager(triggersPath)
  581. tricks   = TrickManager(tricksPath)
  582.  
  583. """
  584. players.addPlayer(2) # Add a player
  585. players[2].userid # we can access PlayerObject attributes
  586. players[2].tricks # We can also access the cache objects
  587. players[2].cache.commit() # Commit the cache, update the new values to the DB if they have altered
  588. players[2].cache.update() # Force a database update, this is done automatically every time a player is created, we shouldn't need to force an update ever
  589. """
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement