You can subscribe to this list here.
2009 |
Jan
|
Feb
(9) |
Mar
(11) |
Apr
|
May
(4) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(9) |
Nov
(5) |
Dec
(18) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2010 |
Jan
(4) |
Feb
|
Mar
|
Apr
(4) |
May
|
Jun
|
Jul
(11) |
Aug
(33) |
Sep
(33) |
Oct
|
Nov
|
Dec
|
From: <Blu...@us...> - 2010-04-24 22:05:43
|
Revision: 338 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=338&view=rev Author: BlueWolf_ Date: 2010-04-24 22:05:37 +0000 (Sat, 24 Apr 2010) Log Message: ----------- Moved the sh a bit and added a event-loop. Use the close-button to exit. Ctrl+c acts random Modified Paths: -------------- trunk/client/VP.py trunk/client/functions.py trunk/client/layout.py trunk/client/playground.py trunk/client/windows.py Modified: trunk/client/VP.py =================================================================== --- trunk/client/VP.py 2010-04-24 21:22:36 UTC (rev 337) +++ trunk/client/VP.py 2010-04-24 22:05:37 UTC (rev 338) @@ -9,43 +9,49 @@ class Main(): def __init__(self): - # This will be available ([sh]ared) to all functions - self.sh = {} - self.blit_lock = threading.Lock() # Fire up pygame os.environ['SDL_VIDEO_CENTERED']='1' pygame.init() pygame.display.set_caption('Virtual Playground') - self.sh['screen'] = pygame.display.set_mode((1000, 700)) + sh['screen'] = pygame.display.set_mode((1000, 700)) #icon = load_image('img','icon.png') #pygame.display.set_icon(icon) pygame.key.set_repeat(250, 40) pygame.scrap.init() - # Give the program a way to close itself. Just call .set() and - # all your problems will be gone - self.sh['close'] = threading.Event() - - # Give the surfaces a way to blit themselves - self.sh['update'] = self.update + sh['update'] = self.update # Create all our surfaces - self.sh['playground'] = Playground(self.sh) - self.sh['layout'] = Layout(self.sh) - self.sh['windows'] = Windows(self.sh) + sh['playground'] = Playground() + sh['layout'] = Layout() + sh['windows'] = Windows() self.update() - - # Wait until someone decides to close VP (Why??) - try: self.sh['close'].wait(31556926) # A timeout of a year, - # should be enough - except: print + # Wait for all the events to come + try: + while 1: + ev = pygame.event.wait() + + if ev.type == QUIT: break + + if ev.type == MOUSEMOTION: + pos = pygame.mouse.get_pos() + pygame.event.clear(MOUSEMOTION) + + # [TODO] Do something + + + except KeyboardInterrupt: # Note: this does not work properly + print + + # Someone decided to close VP (Why??) sys.exit() + def update(self, rect = Rect(0,0, 1000, 700)): @@ -58,9 +64,9 @@ # the same time self.blit_lock.acquire() - self.sh['screen'].blit(self.sh['playground'].surf, rect, rect) - self.sh['screen'].blit(self.sh['layout'].surf, rect, rect) - self.sh['screen'].blit(self.sh['windows'].surf, rect, rect) + sh['screen'].blit(sh['playground'].surf, rect, rect) + sh['screen'].blit(sh['layout'].surf, rect, rect) + sh['screen'].blit(sh['windows'].surf, rect, rect) pygame.display.update(rect) Modified: trunk/client/functions.py =================================================================== --- trunk/client/functions.py 2010-04-24 21:22:36 UTC (rev 337) +++ trunk/client/functions.py 2010-04-24 22:05:37 UTC (rev 338) @@ -2,6 +2,9 @@ from pygame.locals import * import core +# This will be used to [sh]are all variables, functions and classes across the +# code +sh = {} def real_path(*dir): """ Modified: trunk/client/layout.py =================================================================== --- trunk/client/layout.py 2010-04-24 21:22:36 UTC (rev 337) +++ trunk/client/layout.py 2010-04-24 22:05:37 UTC (rev 338) @@ -1,8 +1,7 @@ from functions import * class Layout(): - def __init__(self, sh): - self.sh = sh + def __init__(self): self.surf = pygame.Surface((1000, 700), \ SRCALPHA).convert_alpha() Modified: trunk/client/playground.py =================================================================== --- trunk/client/playground.py 2010-04-24 21:22:36 UTC (rev 337) +++ trunk/client/playground.py 2010-04-24 22:05:37 UTC (rev 338) @@ -1,8 +1,7 @@ from functions import * class Playground(): - def __init__(self, sh): - self.sh = sh + def __init__(self): self.surf = pygame.Surface((1000, 700), \ SRCALPHA).convert_alpha() Modified: trunk/client/windows.py =================================================================== --- trunk/client/windows.py 2010-04-24 21:22:36 UTC (rev 337) +++ trunk/client/windows.py 2010-04-24 22:05:37 UTC (rev 338) @@ -1,7 +1,6 @@ from functions import * class Windows(): - def __init__(self, sh): - self.sh = sh + def __init__(self): self.surf = pygame.Surface((1000, 700), \ SRCALPHA).convert_alpha() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2010-04-24 21:22:42
|
Revision: 337 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=337&view=rev Author: BlueWolf_ Date: 2010-04-24 21:22:36 +0000 (Sat, 24 Apr 2010) Log Message: ----------- I'm starting with the client-app! It actually draws an awesome RECTANGLE right now! Wheee! Added Paths: ----------- trunk/client/VP.py trunk/client/functions.py trunk/client/layout.py trunk/client/playground.py trunk/client/windows.py Added: trunk/client/VP.py =================================================================== --- trunk/client/VP.py (rev 0) +++ trunk/client/VP.py 2010-04-24 21:22:36 UTC (rev 337) @@ -0,0 +1,69 @@ +#!/usr/bin/env python + +from functions import * + +from playground import Playground +from layout import Layout +from windows import Windows + + +class Main(): + def __init__(self): + # This will be available ([sh]ared) to all functions + self.sh = {} + + self.blit_lock = threading.Lock() + + # Fire up pygame + os.environ['SDL_VIDEO_CENTERED']='1' + pygame.init() + pygame.display.set_caption('Virtual Playground') + self.sh['screen'] = pygame.display.set_mode((1000, 700)) + #icon = load_image('img','icon.png') + #pygame.display.set_icon(icon) + pygame.key.set_repeat(250, 40) + pygame.scrap.init() + + # Give the program a way to close itself. Just call .set() and + # all your problems will be gone + self.sh['close'] = threading.Event() + + + # Give the surfaces a way to blit themselves + self.sh['update'] = self.update + + # Create all our surfaces + self.sh['playground'] = Playground(self.sh) + self.sh['layout'] = Layout(self.sh) + self.sh['windows'] = Windows(self.sh) + + self.update() + + + + # Wait until someone decides to close VP (Why??) + try: self.sh['close'].wait(31556926) # A timeout of a year, + # should be enough + except: print + sys.exit() + + + def update(self, rect = Rect(0,0, 1000, 700)): + """ + Blit all the surfaces (playground, layout and windows) together + and update the screen + """ + + # Prevent multiple threads blitting at + # the same time + self.blit_lock.acquire() + + self.sh['screen'].blit(self.sh['playground'].surf, rect, rect) + self.sh['screen'].blit(self.sh['layout'].surf, rect, rect) + self.sh['screen'].blit(self.sh['windows'].surf, rect, rect) + + pygame.display.update(rect) + + self.blit_lock.release() + +if __name__ == "__main__": Main() Property changes on: trunk/client/VP.py ___________________________________________________________________ Added: svn:executable + * Added: trunk/client/functions.py =================================================================== --- trunk/client/functions.py (rev 0) +++ trunk/client/functions.py 2010-04-24 21:22:36 UTC (rev 337) @@ -0,0 +1,27 @@ +import pygame, os, sys, threading +from pygame.locals import * +import core + + +def real_path(*dir): + """ + Get the real path to this file + """ + return os.path.join(sys.path[0], *dir) + +def load_image(alpha, *dir): + """ + This will load an image for pygame + """ + + fullname = real_path(*dir) + try: + image = pygame.image.load(fullname) + if image.get_alpha is None or alpha == False: + image = image.convert() + else: + image = image.convert_alpha() + except pygame.error, message: + print 'Cannot load image:', fullname + raise SystemExit, message + return image Added: trunk/client/layout.py =================================================================== --- trunk/client/layout.py (rev 0) +++ trunk/client/layout.py 2010-04-24 21:22:36 UTC (rev 337) @@ -0,0 +1,9 @@ +from functions import * + +class Layout(): + def __init__(self, sh): + self.sh = sh + self.surf = pygame.Surface((1000, 700), \ + SRCALPHA).convert_alpha() + + self.surf.fill([0,100,0], Rect(100,100, 150,150)) Added: trunk/client/playground.py =================================================================== --- trunk/client/playground.py (rev 0) +++ trunk/client/playground.py 2010-04-24 21:22:36 UTC (rev 337) @@ -0,0 +1,9 @@ +from functions import * + +class Playground(): + def __init__(self, sh): + self.sh = sh + self.surf = pygame.Surface((1000, 700), \ + SRCALPHA).convert_alpha() + + self.surf.fill([0,0,0]) Added: trunk/client/windows.py =================================================================== --- trunk/client/windows.py (rev 0) +++ trunk/client/windows.py 2010-04-24 21:22:36 UTC (rev 337) @@ -0,0 +1,7 @@ +from functions import * + +class Windows(): + def __init__(self, sh): + self.sh = sh + self.surf = pygame.Surface((1000, 700), \ + SRCALPHA).convert_alpha() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2010-04-21 15:18:44
|
Revision: 336 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=336&view=rev Author: BlueWolf_ Date: 2010-04-21 15:18:38 +0000 (Wed, 21 Apr 2010) Log Message: ----------- Added callback.leaves_vp. callback.enters_vp and callback.leaves_vp both have a cid as argument now. Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2010-04-19 19:44:41 UTC (rev 335) +++ trunk/server/core/callback.py 2010-04-21 15:18:38 UTC (rev 336) @@ -203,10 +203,18 @@ """ return True - def enters_vp(self, client): + def enters_vp(self, cid): """ Called when a client is logged in and enters VP. - client: - Same as server.clients[cid] + cid: + The unique ID for this connection. (connection-id) """ + + def leaves_vp(self, cid): + """ + Called when a logged-in client has gone offline. + + cid: + The unique ID for this connection. (connection-id) + """ Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2010-04-19 19:44:41 UTC (rev 335) +++ trunk/server/core/parser.py 2010-04-21 15:18:38 UTC (rev 336) @@ -71,7 +71,7 @@ def _is_client_visible(self, client1, client2): """ - Checks is client1 can see client2 + Checks if client1 can see client2 """ client1 = client1['pos'] @@ -100,7 +100,7 @@ Send when the users wants to log in usr - The username pwd - The (rsa encrypted) sha-password - bot - Whether the it's an bot or an user + bot - Whether it's an bot or an user for - For what services it's logging in "VP" - VP client client - list with [app_name, app_version] @@ -262,5 +262,5 @@ data.send("userlist", userlist) - self.call.enters_vp(client) + self.call.enters_vp(cid) Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2010-04-19 19:44:41 UTC (rev 335) +++ trunk/server/core/server.py 2010-04-21 15:18:38 UTC (rev 336) @@ -270,16 +270,16 @@ # Was this user already online? client = self.__clients[self.__cid] if client['status'] == 'VP': - + # Tell other users this one has gone offline for cid, cl in self.__clients.items(): if cid == self.__cid: continue + if cl['status'] == 'VP': + cl['con'].send("useroffline", { + "cid": self.__cid, + "reason": reason + }) - cl['con'].send("useroffline", { - "cid": self.__cid, - "reason": reason - }) - def run(self): """ @@ -341,6 +341,11 @@ except: pass self.__sock.close() + # Was this user already online? + client = self.__clients[self.__cid] + if client['status'] == 'VP': + self.__call.leaves_vp(self.__cid) + self.__call.connection_closed(self.__cid, reason) self.__notify_offline(reason) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2010-04-19 19:44:47
|
Revision: 335 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=335&view=rev Author: BlueWolf_ Date: 2010-04-19 19:44:41 +0000 (Mon, 19 Apr 2010) Log Message: ----------- Some small modifications. sh['login_after_connection'] => sh['auto_login']. Will remember auto_login regardless of login being called before or after receiving the rsa-key Modified Paths: -------------- trunk/client/core/client.py trunk/client/core/parser.py Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2010-01-08 23:44:25 UTC (rev 334) +++ trunk/client/core/client.py 2010-04-19 19:44:41 UTC (rev 335) @@ -79,7 +79,7 @@ self.__do_connect.clear() # Auto log in after connecting. - self.__sh['login_after_connecting'] = () + self.__sh['auto_login'] = () # RSA self.__sh['rsakey'] = None @@ -190,12 +190,12 @@ if self.cid != None: raise LoginError("You are already logged in") - if not self.__sh['rsakey']: - # Save this - self.__sh['login_after_connecting'] = (self.login, - (usr, pwd)) - return + # Save this + self.__sh['auto_login'] = (self.login, (usr, pwd)) + # Wait for the rsa-key to arrive + if not self.__sh['rsakey']: return + if not self.__sock: raise ConnectionError("You are not connected!") @@ -229,12 +229,13 @@ if self.cid != None: raise LoginError("You are already logged in") - if not self.__sh['rsakey']: - # Save this - self.__sh['login_after_connecting'] = (self.login_bot, + # Save this + self.__sh['auto_login'] = (self.login_bot, \ (owner, pwd, screenname)) - return + # Wait for the rsa-key to arrive + if not self.__sh['rsakey']: return + if not self.__sock: raise ConnectionError("You are not connected!") @@ -274,7 +275,7 @@ def close(self, reason = "manual"): """ - Will close the connection to the server. If the connection in + Will close the connection to the server. If the connection is already down, it will raise an exception. This will trigger the callback `disconnected`. It may reconnect, depending on the callback. If not, you can reuse this class by calling `connect`. @@ -311,7 +312,7 @@ self.__do_connect.set() else: # Remove auto-login - self.__sh['login_after_connecting'] = () + self.__sh['auto_login'] = () class __Pinger(): Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2010-01-08 23:44:25 UTC (rev 334) +++ trunk/client/core/parser.py 2010-04-19 19:44:41 UTC (rev 335) @@ -44,7 +44,6 @@ "crash" - The server-parser has made a crash "gone offline" - Server has just shutdown """ - # reason - Why it's going to disconnect us self.core.close(msg["reason"]) @@ -59,8 +58,9 @@ self.call.received_rsa(msg['public']) - if self.sh['login_after_connecting'] != (): - login = self.sh['login_after_connecting'] + if self.core.cid == None and \ + self.sh['auto_login'] != (): + login = self.sh['auto_login'] login[0](*login[1]) @@ -125,7 +125,17 @@ pass + def useroffline(self, msg): + """ + Some user has gone offline + * cid - The connection-id for this client + """ + + # TODO + + pass + def error(self, msg): """ Called when we did something wrong! This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ch...@us...> - 2010-01-08 23:44:35
|
Revision: 334 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=334&view=rev Author: chozone Date: 2010-01-08 23:44:25 +0000 (Fri, 08 Jan 2010) Log Message: ----------- [VPS-core] fix check_version (used old def name... :$ ) Modified Paths: -------------- trunk/server/core/functions.py Modified: trunk/server/core/functions.py =================================================================== --- trunk/server/core/functions.py 2010-01-08 23:02:27 UTC (rev 333) +++ trunk/server/core/functions.py 2010-01-08 23:44:25 UTC (rev 334) @@ -119,8 +119,8 @@ # Use this function to see if it's higher than the first, and # lower than the second value :-) - if _check_version(version, '>' + r1) \ - and _check_version(version, '<' + r2): + if check_version(version, '>' + r1) \ + and check_version(version, '<' + r2): return True # Better luck next time :-( This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ch...@us...> - 2010-01-08 23:02:35
|
Revision: 333 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=333&view=rev Author: chozone Date: 2010-01-08 23:02:27 +0000 (Fri, 08 Jan 2010) Log Message: ----------- [VPS-core] Added functions.py (global functions). Added check_version function, to that file. Modified Paths: -------------- trunk/server/core/parser.py trunk/server/core/server.py Added Paths: ----------- trunk/server/core/functions.py Added: trunk/server/core/functions.py =================================================================== --- trunk/server/core/functions.py (rev 0) +++ trunk/server/core/functions.py 2010-01-08 23:02:27 UTC (rev 333) @@ -0,0 +1,129 @@ +## This file is part of Virtual Playground +## Copyright (c) 2009 Jos Ratsma + Koen Koning + +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either +## version 2 of the License, or (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +def check_version(version, expr): + """ + Checks whether a version meets certain conditions. + The first argument, `version`, contains the version you want to check. + The second argument, `expr`, contains the condition (expression). + + Examples of some expression are: + + Exact match: + '1.0.3' match ONLY that version (True if version equal given) + + Greater than: + '>0.9.48' match all versions above version '0.9.48', AND '0.9.48' + itself (True if same or above given) + + Lower than: + '<10.4.0' match all versions below version '10.4.0', AND '10.4.0' + itself (True if same or below given) + + Range: + '0.9.3-0.9.11' match all versions between '0.9.3' and '0.9.11', + including those values themselves (True if same or + between two values) + + + If all conditions are met, this function will return True, otherwise + False. This function will raise a ValueError in case that there were + errors in the provided version/expression. + + As an extra note, remember that 1.0.2 is lower than 1.0.10 (10 > 2). In + other words, there is no preceding zero. + This is because it's not a decimal number. + """ + + # Filter out invalid versions/expressions, so this function works + # correctly later on. + if not expr: return True + if not version: return False + version = str(version) + expr = str(expr) + + try: [int(i) for i in version.split('.')] + except ValueError: raise ValueError, "No valid version: '%s'"%version + + try: [[int(i) for i in p.split('.')] \ + for p in expr.strip('<>').split('-')] + except ValueError: raise ValueError, "No valid expression: '%s'"%expr + + + # Our first real tests. If version matches expression, we return True. + # Also, if the expression, stripped of '<' and '>' characters at the + # beginning (or end...) matched our version, we return True. + # This is for the 'Greater/Lower OR EQUAL than' behaviour of those + # characters. + if version == expr: return True + if version == expr.strip('<>'): return True + + if expr[0] == '>': + # Greater than + + # Get rid of the signs, so we have normal version to work with. + expr = expr.strip('<>') + + # Save the three int that make up the version into an array, so + # we can access them easily to do our check, a few lines below. + c1 = [int(i) for i in version.split('.')] + c2 = [int(i) for i in expr.split('.')] + + # Loops through major, minor, revision, in that order, and + # it tests whether the version is higher/lower. Note that if the + # version are the same, it has already been filtered out, before + # this if block. + for p1, p2 in zip(c1, c2): + if p1 > p2: return True + if p1 < p2: return False + elif expr[0] == '<': + # Less than + + # Mostly same as above + + # Get rid of the signs, so we have normal version to work with. + expr = expr.strip('<>') + + # Save the three int that make up the version into an array, so + # we can access them easily to do our check, a few lines below. + c1 = [int(i) for i in version.split('.')] + c2 = [int(i) for i in expr.split('.')] + + # Loops through major, minor, revision, in that order, and + # it tests whether the version is higher/lower. Note that if the + # version are the same, it has already been filtered out, before + # this if block. + for p1, p2 in zip(c1, c2): + if p1 < p2: return True + if p1 > p2: return False + elif expr.find('-') > -1: + # Range + + # Get the two versions + r1, r2 = expr.split('-') + + # Use this function to see if it's higher than the first, and + # lower than the second value :-) + if _check_version(version, '>' + r1) \ + and _check_version(version, '<' + r2): + return True + + # Better luck next time :-( + return False + + Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2010-01-03 16:04:22 UTC (rev 332) +++ trunk/server/core/parser.py 2010-01-08 23:02:27 UTC (rev 333) @@ -15,8 +15,10 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +from functions import * import rsa, hashlib + class Parser(): def __init__(self, sh): """ Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2010-01-03 16:04:22 UTC (rev 332) +++ trunk/server/core/server.py 2010-01-08 23:02:27 UTC (rev 333) @@ -16,9 +16,12 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. import simplejson, socket, threading, time, random, sys, hashlib + +from functions import * from parser import Parser import rsa + class Server(threading.Thread): """ This is the server-core for Virtual Playground. This will handle all This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2010-01-03 16:04:29
|
Revision: 332 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=332&view=rev Author: BlueWolf_ Date: 2010-01-03 16:04:22 +0000 (Sun, 03 Jan 2010) Log Message: ----------- [Client-core] It will send it's protocol-version Modified Paths: -------------- trunk/client/core/client.py trunk/client/core/parser.py Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2010-01-03 16:04:11 UTC (rev 331) +++ trunk/client/core/client.py 2010-01-03 16:04:22 UTC (rev 332) @@ -212,7 +212,8 @@ "for": "VP", "client": ( self.__sh['config']['app_name'], self.__sh['config']['app_version'] - ) + ), + "version": __version__ }) def login_bot(self, owner, pwd, screenname): @@ -251,7 +252,8 @@ "for": "VP", "client": ( self.__sh['config']['app_name'], self.__sh['config']['app_version'] - ) + ), + "version": __version__ }) Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2010-01-03 16:04:11 UTC (rev 331) +++ trunk/client/core/parser.py 2010-01-03 16:04:22 UTC (rev 332) @@ -87,6 +87,45 @@ self.call.logged_in(msg['username'], msg['cid'], None) + + def userlist(self, msg): + """ + Called after we've logged it. This contains a list with all + online users. This is a _list_ with: + * cid - The unique ID for this connection + * user - The username + * app - [Appname, Appversion] + * version - Which core it's using (string) + * bot - If it's a bot + * owner - The owner for this bot (only available when + bot = True) + * pos - [Z, X, Y] The position for this user. Is None + when you can't see this user. + """ + + # TODO + + pass + + def useronline(self, msg): + """ + Called after a user (or bot) has signed on. + * cid - The unique ID for this connection + * user - The username + * app - [Appname, Appversion] + * version - Which core it's using (string) + * bot - If it's a bot + * owner - The owner for this bot (only available when + bot = True) + * pos - [Z, X, Y] The position for this user. Is None + when you can't see this user. + """ + + # TODO + + pass + + def error(self, msg): """ Called when we did something wrong! This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2010-01-03 16:04:18
|
Revision: 331 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=331&view=rev Author: BlueWolf_ Date: 2010-01-03 16:04:11 +0000 (Sun, 03 Jan 2010) Log Message: ----------- [Server-core] It will now save the client app/version, protocolversion and client position. On login, it also will send a list of all connected clients with position (if allowed). When going offline, it will notify all other clients too Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-23 13:17:00 UTC (rev 330) +++ trunk/server/core/callback.py 2010-01-03 16:04:11 UTC (rev 331) @@ -202,3 +202,11 @@ overwrite this in your own callback. """ return True + + def enters_vp(self, client): + """ + Called when a client is logged in and enters VP. + + client: + Same as server.clients[cid] + """ Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-23 13:17:00 UTC (rev 330) +++ trunk/server/core/parser.py 2010-01-03 16:04:11 UTC (rev 331) @@ -67,7 +67,32 @@ return True + def _is_client_visible(self, client1, client2): + """ + Checks is client1 can see client2 + """ + + client1 = client1['pos'] + client2 = client2['pos'] + + # Check height + if client1[2] != client2[2]: return False + + # Check horizontal + if client1[0]-client2[0] > self.sh['config']['viewport'][0]/2 \ + or client1[0]-client2[0] < -self.sh['config']['viewport'][0]/2: + return False + + # Check vertical + if client1[1]-client2[1] > self.sh['config']['viewport'][1]/2 \ + or client1[1]-client2[1] < -self.sh['config']['viewport'][1]/2: + return False + + # Clients see each other! + return True + + def login(self, cid, msg): """ Send when the users wants to log in @@ -77,6 +102,7 @@ for - For what services it's logging in "VP" - VP client client - list with [app_name, app_version] + version - string with (protocol) version (xx.xx.xx) """ client = self.clients[cid] @@ -108,8 +134,8 @@ bots = [] for id, cl in self.clients.items(): if cl['status'] == 'VP' and \ - cl['owner'] == username and \ - cl['bot'] == True: + cl['bot'] == True and \ + cl['owner'] == username: bots.append(id) # Get screenname @@ -132,40 +158,107 @@ + # Check for double logins if msg['bot'] == False: # Just an ordinary user - if self._check_double_user(False, username, data) == False: return - - - # Log the user in - client['user'] = screenname + + else: # Client is a bot + if self._check_double_user(True, screenname, + data) == False: + return + + + # Log the client in + + client['user'] = screenname + client['app'] = tuple(msg['client']) + client['version'] = str(msg['version']) + client['pos'] = [0, 0, 0] # XYZ + client['status'] = "VP" + + if msg['bot'] == False: client['bot'] = False - client['status'] = "VP" data.send("login", { "username": username, "cid": cid }) - else: # Client is bot - - if self._check_double_user(True, screenname, - data) == False: - return - - - # log the bot in - client['user'] = screenname + else: client['bot'] = True client['owner'] = username - client['status'] = "VP" data.send("login", { "username": screenname, "owner": username, "cid": cid }) + + + # TODO: Send world + + + + user = {} # This will be send to others + user['cid'] = cid + user['user'] = client['user'] + user['app'] = client['app'] + user['version'] = client['version'] + user['bot'] = client['bot'] + if client['bot']: user['owner'] = client['owner'] + + # Send online clients + userlist = [] + + for cid_o, client_o in self.clients.items(): + if client_o['status'] != "VP": continue + # To clear some things up: + # client = local user info + # cid = local user cid + # client_o = other user info + # cid_o = other user cid + # + # user = local user info that will send to + # client_o + # user_o = other user info that will send to + # client + + user_o = {} + + user_o['cid'] = cid_o + user_o['user'] = client_o['user'] + user_o['app'] = client_o['app'] + user_o['version'] = client_o['version'] + + user_o['bot'] = client_o['bot'] + if client_o['bot']: + user_o['owner'] = client_o['owner'] + + # Positions + # Is this user visible for this client? + if self._is_client_visible(client, client_o): + user_o['pos'] = client_o['pos'] + user['pos'] = client['pos'] + else: + user_o['pos'] = None + user['pos'] = None + + + # Send data to other user + if cid != cid_o: + client_o['con'].send("useronline", user) + + # Send data to this user + userlist.append(user_o) + + + # Send list + data.send("userlist", userlist) + + + self.call.enters_vp(client) + Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-23 13:17:00 UTC (rev 330) +++ trunk/server/core/server.py 2010-01-03 16:04:11 UTC (rev 331) @@ -72,6 +72,12 @@ logs in as "test", it will be displayed as "[test]". Choose something that won't interfere with existing users. + + -viewport + A tuple of the client's viewport in pixels. This is very + important because it's used to calculate which objects + and people the client is able to see. By default, this + is (1000, 700). """ @@ -79,7 +85,7 @@ self.__sock = None self.__call = callback_class - # This will be available ([sh]ared) to al modules in the core + # This will be available ([sh]ared) to all modules in the core self.__sh = {} self.__sh['call'] = callback_class @@ -104,6 +110,7 @@ config.get('max_connections', 0)) config['rsa_bits'] = int(config.get('rsa_bits', 64)) config['bot_names'] = str(config.get('bot_names', "[%s]")) + config['viewport'] = tuple(config.get('viewport', (1000,700))) def start_server(self): @@ -248,7 +255,29 @@ traceback += ev.__class__.__name__ + ': ' + str(ev) self.__call.debug_crash(traceback) + + def __notify_offline(self, reason): + """ + Used by close and close_msg to notify other users this client + has gone offline. + """ + if reason == "gone offline": return # Server is going offline + + # Was this user already online? + client = self.__clients[self.__cid] + if client['status'] == 'VP': + + # Tell other users this one has gone offline + for cid, cl in self.__clients.items(): + if cid == self.__cid: continue + + cl['con'].send("useroffline", { + "cid": self.__cid, + "reason": reason + }) + + def run(self): """ Used by threading, not for external usage. @@ -311,6 +340,8 @@ self.__call.connection_closed(self.__cid, reason) + self.__notify_offline(reason) + del self.__clients[self.__cid] def close_msg(self, reason): @@ -329,6 +360,9 @@ self.__sock.close() self.__call.connection_closed(self.__cid, reason) + + self.__notify_offline(reason) + del self.__clients[self.__cid] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-23 13:17:17
|
Revision: 330 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=330&view=rev Author: BlueWolf_ Date: 2009-12-23 13:17:00 +0000 (Wed, 23 Dec 2009) Log Message: ----------- Client can now log in as bot. The core is now using a dict to share variables across all modules Modified Paths: -------------- trunk/client/core/callback.py trunk/client/core/client.py trunk/client/core/parser.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-23 13:16:28 UTC (rev 329) +++ trunk/client/core/callback.py 2009-12-23 13:17:00 UTC (rev 330) @@ -109,7 +109,7 @@ """ pass - def logged_in(self, username, cid): + def logged_in(self, username, cid, owner): """ Called when we are logged in. @@ -120,6 +120,9 @@ cid: The unique client-id for this connection. Also available in client.cid + owner: + This is the owner-username for this bot. This variable + will be None when you log in as a normal user This is a placeholder. If you want to catch this event, overwrite this in your own callback @@ -139,6 +142,8 @@ * "bot limit reached" - There're to much bots logged in for your account * "login blocked" - You (or your IP) are blocked + * "duplicate user" - There is already someone + online with this username """ pass Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2009-12-23 13:16:28 UTC (rev 329) +++ trunk/client/core/client.py 2009-12-23 13:17:00 UTC (rev 330) @@ -58,32 +58,40 @@ self.__sock = None self.__pinger = None + # This will be available ([sh]ared) to al modules in the core + self.__sh = {} + + self.__sh['call'] = callback_class + self.__sh['core'] = self + self.__sh['isbot'] = None + # Create all default settings self.__config_default(config) - self.__config = config + self.__sh['config'] = config - # Class that parsers all incomming data - self.__parse = Parser(self.__call, self) - # So the client knows if we are online. Is used for # disconnecting - self.__is_online = False + self.__sh['is_online'] = False # Treading-event so that run() waits before it's allowed to # connect self.__do_connect = threading.Event() self.__do_connect.clear() - # Auto log in after connecting. Used by self.connect - self.__login_after_connecting = () + # Auto log in after connecting. + self.__sh['login_after_connecting'] = () # RSA - self.__rsakey = None + self.__sh['rsakey'] = None # Info after logging in self.username = None + self.owner = None self.cid = None # Connection-id + # Class that parsers all incomming data + self.__parse = Parser(self.__sh) + threading.Thread.__init__(self) self.setDaemon(True) @@ -91,18 +99,19 @@ def __config_default(self, config): - config.setdefault('app_name', __name__) - config.setdefault('app_version', __version__) + config['app_name'] = str(config.get('app_name', __name__)) + config['app_version'] = str(config.get('app_version', + __version__)) - def connect(self, host, port, usr = None, pwd = None, bot = False): + def connect(self, host, port): """ Will (try to) connect to the server on `host`:`port`. If you want to reconnect, you have to close the connection first, if it's still open. - You can optionally let it login automatically. See client.login - for the usr, pwd and bot variables. + You can to Client.login and Client.bot_login directly after + this. It will be done as soon as it has received a rsa-key. """ self.__host = host @@ -111,13 +120,6 @@ if self.__sock: raise ConnectionError("You are already connected!") - # Should we login after connecting? - if usr and pwd: - self.__login_after_connecting = (usr, pwd, bot) - else: - self.__login_after_connecting = (); - - self.__do_connect.set() # Releasing the hounds @@ -128,7 +130,7 @@ while 1: self.__do_connect.wait() # Wait to connect - self.__is_online = True + self.__sh['is_online'] = True disconnect_reason = None self.__sock = socket.socket(socket.AF_INET, @@ -137,7 +139,7 @@ while 1: try: self.__sock.connect((self.__host, - self.__port)) + self.__port)) break except: time.sleep(1) @@ -171,15 +173,14 @@ for msg in data: self.__parse(simplejson.loads(msg)) - if self.__is_online: + if self.__sh['is_online']: self.close(disconnect_reason) - def login(self, usr, pwd, bot = False): + def login(self, usr, pwd): """ - Log in to the server. You should have received the RSA-key - before calling this! - pwd is expected to be sha1. Bot should be True or False. Bots - have different functions in VP. + Log in to the server as a normal user. If the RSA-key wasn't + received yet, it will login after is has received the key. pwd + is expected to be sha1. >>> import hashlib >>> hashlib.sha1("MyVerySecretPassword").hexdigest() @@ -189,34 +190,71 @@ if self.cid != None: raise LoginError("You are already logged in") - # Get our login - if not (usr and pwd): - if self.__login_after_connecting: - usr, pwd, bot = self.__login_after_connecting - else: - return # Just ignore it.. + if not self.__sh['rsakey']: + # Save this + self.__sh['login_after_connecting'] = (self.login, + (usr, pwd)) + return if not self.__sock: raise ConnectionError("You are not connected!") - if not self.__rsakey: - raise LoginError("No rsa-key available") + self.__sh['isbot'] = False # Convert pwd - pwd = rsa.encrypt(pwd, self.__rsakey) + pwd = rsa.encrypt(pwd, self.__sh['rsakey']) # Log in self.send("login", { "usr": usr, "pwd": pwd, - "bot": bool(bot), + "bot": False, "for": "VP", - "client": ( self.__config['app_name'], - self.__config['app_version'] + "client": ( self.__sh['config']['app_name'], + self.__sh['config']['app_version'] ) }) + def login_bot(self, owner, pwd, screenname): + """ + Log in as a bot. If the RSA-key wasn't received yet, it will + login after is has received the key. pwd is expected to be sha1. + + >>> import hashlib + >>> hashlib.sha1("MyVerySecretPassword").hexdigest() + 'dc2275e9f1e53926dce503ec7d9df9ac9ce07dfc' + """ + + if self.cid != None: + raise LoginError("You are already logged in") + + if not self.__sh['rsakey']: + # Save this + self.__sh['login_after_connecting'] = (self.login_bot, + (owner, pwd, screenname)) + return + + if not self.__sock: + raise ConnectionError("You are not connected!") + + self.__sh['isbot'] = True + + # Convert pwd + pwd = rsa.encrypt(pwd, self.__sh['rsakey']) + + # Log in + self.send("login", { + "usr": owner, + "pwd": pwd, + "name": screenname, + "bot": True, + "for": "VP", + "client": ( self.__sh['config']['app_name'], + self.__sh['config']['app_version'] + ) + }) + def send(self, data_header, data_body = {}): """ Sends `data_body` of type `data_header` to the server. It will @@ -240,7 +278,7 @@ callback. If not, you can reuse this class by calling `connect`. """ - if not self.__is_online: + if not self.__sh['is_online']: if reason == "manual": # Annoy the user raise ConnectionError("The connection is " + \ @@ -251,13 +289,14 @@ self.__do_connect.clear() # Block the thread-loop - self.__is_online = False + self.__sh['is_online'] = False self.__pinger.cancel() self.__pinger = None # Reset variables - self.__rsa = None + self.__sh['rsakey'] = None self.username = None + self.owner = None self.cid = None try: self.__sock.shutdown(0) @@ -268,6 +307,9 @@ if self.__call.disconnected(reason): # Reconnect self.__do_connect.set() + else: + # Remove auto-login + self.__sh['login_after_connecting'] = () class __Pinger(): @@ -288,7 +330,7 @@ if self.running == False: return #Ping! - self.send('PNG', {}) + self.send('ping', {}) #Start again self.timer = threading.Timer(30, self.run) Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-23 13:16:28 UTC (rev 329) +++ trunk/client/core/parser.py 2009-12-23 13:17:00 UTC (rev 330) @@ -16,15 +16,16 @@ ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. class Parser(): - def __init__(self, callback, client): + def __init__(self, sh): """ This class parses all received messages from the server. """ - self.callback = callback - self.client = client + self.sh = sh + self.call = sh['call'] + self.core = sh['core'] def __call__(self, msg): - self.callback.data_received(msg) + self.call.data_received(msg) head = msg.keys()[0] body = msg[head] @@ -45,7 +46,7 @@ """ # reason - Why it's going to disconnect us - self.client.close(msg["reason"]) + self.core.close(msg["reason"]) def rsa(self, msg): """ @@ -54,23 +55,37 @@ * public - The public rsa-key, which contains e & n """ - self.client._Client__rsakey = msg['public'] + self.sh['rsakey'] = msg['public'] - self.callback.received_rsa(msg['public']) + self.call.received_rsa(msg['public']) - self.client.login(None, None) + if self.sh['login_after_connecting'] != (): + login = self.sh['login_after_connecting'] + login[0](*login[1]) + def login(self, msg): """ Called when we can log in * username - The username (with right caps) * cid - The server's connection-id + * owner - The owner for this bot (only when logging in + as bot!) """ - self.client.username = msg['username'] - self.client.cid = msg['cid'] + if self.sh['isbot']: # Logging in as bot + self.core.owner = msg['owner'] + self.core.username = msg['username'] + self.core.cid = msg['cid'] + + self.call.logged_in(msg['username'], msg['cid'], + msg['owner']) - self.callback.logged_in(msg['username'], msg['cid']) + else: # Logging in as user + self.core.username = msg['username'] + self.core.cid = msg['cid'] + + self.call.logged_in(msg['username'], msg['cid'], None) def error(self, msg): """ @@ -80,14 +95,18 @@ "login not allowed" - User may not log in right now "bot limit reached" - Too much bots logged in for this user "login blocked" - The user (or IP) is blocked + "duplicate user" - There is already someone online with + this username """ if msg['reason'] == "bad login": - self.callback.failed_logging_in("bad login") + self.call.failed_logging_in("bad login") elif msg['reason'] == "login not allowed": - self.callback.failed_logging_in("login not allowed") + self.call.failed_logging_in("login not allowed") elif msg['reason'] == "bot limit reached": - self.callback.failed_logging_in("bot limit reached") + self.call.failed_logging_in("bot limit reached") elif msg['reason'] == "login blocked": - self.callback.failed_logging_in("login blocked") + self.call.failed_logging_in("login blocked") + elif msg['reason'] == "duplicate user": + self.call.failed_logging_in("duplicate user") This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-23 13:16:43
|
Revision: 329 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=329&view=rev Author: BlueWolf_ Date: 2009-12-23 13:16:28 +0000 (Wed, 23 Dec 2009) Log Message: ----------- Client can now log in as bot! The core is now using a dict to share variables across all modules. Fixing a bot where a client sometimes couldn't log in. Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-19 20:59:37 UTC (rev 328) +++ trunk/server/core/callback.py 2009-12-23 13:16:28 UTC (rev 329) @@ -170,7 +170,7 @@ return False - def may_login(self, username, ip, bot, bots): + def may_login(self, username, screenname, ip, bot, bots): """ This is called after (a succesfull) callback.check_login. Use this to block certain users or IP's or block an additional bot @@ -179,13 +179,17 @@ Return True when the user (or bot) may log in and False when it may not. This will send an "not allowed" error. If you wish to send an other error, return the error. An example may be: - * "login not allowed" - * "bot limit reached" - * "login blocked" + * "login not allowed" - Login is not allowed right now + * "bot limit reached" - User has to much bots running + * "login blocked" - User is blocked + This function will return True by default. username: - The username for this user + The username for this user / owner for this bot + screenname: + The name the users see. This is only different from + username when a bot logs in ip: The IP-adress this user has bot: Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-19 20:59:37 UTC (rev 328) +++ trunk/server/core/parser.py 2009-12-23 13:16:28 UTC (rev 329) @@ -18,18 +18,19 @@ import rsa, hashlib class Parser(): - def __init__(self, callback, server): + def __init__(self, sh): """ This class parses all received messages from the client. """ - self.callback = callback - self.server = server + self.sh = sh + self.call = sh['call'] + self.core = sh['core'] # Shortkeys - self.clients = self.server.clients + self.clients = self.core.clients def __call__(self, cid, msg): - self.callback.data_received(cid, msg) + self.call.data_received(cid, msg) head = msg.keys()[0] body = msg[head] @@ -38,7 +39,35 @@ if (func): func(cid, body) + def _check_double_user(self, isbot, screenname, data): + """ + Checks for an user or bot with the same name + """ + + # Check if there isn't already a bot online + # with this name + for id, cl in self.clients.items(): + if cl['status'] == "VP" and \ + cl['bot'] == True and \ + cl['user'] == screenname: + # Our client cannot log in + data.send("error", { + "reason": "duplicate user" + }) + return False + + if isbot == False: + # Check for the same user + for id, cl in self.clients.items(): + if cl['status'] == "VP" and \ + cl['bot'] == False and \ + cl['user'] == screenname: + # Disconnect this user + cl['con'].close_msg('duplicate') + + return True + def login(self, cid, msg): """ Send when the users wants to log in @@ -64,9 +93,9 @@ pwd = hashlib.sha1(pwd).hexdigest() if msg['for'] == 'VP': - + # Check for login - username = self.callback.check_login(msg['usr'], pwd) + username = self.call.check_login(msg['usr'], pwd) if username == False: # No login, bad user! @@ -77,15 +106,21 @@ # Find out how many bots this user has running bots = [] - for id, client in self.clients.items(): - if client['status'] == 'VP' and \ - client['user'] == username and \ - client['bot'] == True: + for id, cl in self.clients.items(): + if cl['status'] == 'VP' and \ + cl['owner'] == username and \ + cl['bot'] == True: bots.append(id) + # Get screenname + screenname = username + if msg['bot']: + screenname = self.sh['config']['bot_names'] % \ + msg['name'] + # Check if the user may log in - may_login = self.callback.may_login(username, - data.get_ip(), msg['bot'], bots) + may_login = self.call.may_login(username, + screenname, data.get_ip(), msg['bot'], bots) if may_login != True: # User may not login if may_login == False: @@ -99,20 +134,14 @@ if msg['bot'] == False: # Just an ordinary user - - # Check if the user is already logged in - for id, cl in self.clients.items(): - if cl['status'] == "VP" and \ - cl['bot'] == False and \ - cl['user'] == username: - # Disconnect user - cl['con'].send("disconnect", - {'reason':'duplicate'}) - cl['con'].close('duplicate') - - + + if self._check_double_user(False, username, + data) == False: + return + + # Log the user in - client['user'] = username + client['user'] = screenname client['bot'] = False client['status'] = "VP" @@ -122,5 +151,21 @@ }) else: # Client is bot - pass #TODO + if self._check_double_user(True, screenname, + data) == False: + return + + + # log the bot in + client['user'] = screenname + client['bot'] = True + client['owner'] = username + client['status'] = "VP" + + data.send("login", { + "username": screenname, + "owner": username, + "cid": cid + }) + Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-19 20:59:37 UTC (rev 328) +++ trunk/server/core/server.py 2009-12-23 13:16:28 UTC (rev 329) @@ -65,6 +65,13 @@ -rsa_bits: How many bits the rsa uses for encrypting the password. More = more secure but slower to generate. Default is 64 + + -bot_names + How the name for a bot should be. Use %s where the + suggested name should be. Default is [%s]. When a bot + logs in as "test", it will be displayed as "[test]". + Choose something that won't interfere with existing + users. """ @@ -72,12 +79,18 @@ self.__sock = None self.__call = callback_class + # This will be available ([sh]ared) to al modules in the core + self.__sh = {} + + self.__sh['call'] = callback_class + self.__sh['core'] = self + # Create all default settings self.__config_default(config) - self.__config = config + self.__sh['config'] = config self.clients = {} - self.__parse = Parser(self.__call, self) + self.__parse = Parser(self.__sh) threading.Thread.__init__(self) @@ -85,10 +98,12 @@ """ Create all the config-defaults """ - config.setdefault('host', '') - config.setdefault('port', 5162) - config.setdefault('max_connections', 0) - config.setdefault('rsa_bits', 64) + config['host'] = str(config.get('host', '')) + config['port'] = int(config.get('port', 5162)) + config['max_connections'] = int( + config.get('max_connections', 0)) + config['rsa_bits'] = int(config.get('rsa_bits', 64)) + config['bot_names'] = str(config.get('bot_names', "[%s]")) def start_server(self): @@ -119,8 +134,8 @@ #Try to claim the pre-specified port while 1: try: - self.__sock.bind((self.__config['host'], - self.__config['port'])) + self.__sock.bind((self.__sh['config']['host'], + self.__sh['config']['port'])) break except: time.sleep(1) @@ -135,14 +150,14 @@ # Server has gone offline return - if self.__config['max_connections'] and \ + if self.__sh['config']['max_connections'] and \ len(self.clients) >= \ - self.__config['max_connections']: + self.__sh['config']['max_connections']: #We exceeded our connection limit, but maybe #this is a special occasion? Send callback! if not self.__call.connection_limit_exceeded( len(self.clients), - self.__config['max_connections'], + self.__sh['config']['max_connections'], addr[0], addr[1]): #We're full, kick him out @@ -164,9 +179,8 @@ self.clients[cid] = { "status": "", - "con": Client(cid, sock, self.clients, - self.__call, self.__parse, - self.__config) + "con": Client(cid, sock, self.__sh, + self.__parse) } if self.__call.connection_opened(cid, @@ -202,14 +216,14 @@ Each client has it's own class. """ - def __init__(self, cid, sock, clients, callback, parser, config): + def __init__(self, cid, sock, sh, parser): self.__cid = cid self.__sock = sock - self.__clients = clients - self.__call = callback + self.__sh = sh self.__parser = parser - self.__config = config + self.__clients = sh['core'].clients + self.__call = sh['call'] self.is_online = True threading.Thread.__init__(self) @@ -247,7 +261,7 @@ # Before the client can log in, we first need to create a # rsa-key public, private = rsa.gen_pubpriv_keys( - self.__config['rsa_bits']) + self.__sh['config']['rsa_bits']) self.__clients[self.__cid]['rsa'] = private self.send("rsa", {"public": public}) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-19 20:59:46
|
Revision: 328 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=328&view=rev Author: BlueWolf_ Date: 2009-12-19 20:59:37 +0000 (Sat, 19 Dec 2009) Log Message: ----------- Login-errors will go through "error" instead of "login". Added some other login-errors in the documentation Modified Paths: -------------- trunk/client/core/callback.py trunk/client/core/parser.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-19 20:59:27 UTC (rev 327) +++ trunk/client/core/callback.py 2009-12-19 20:59:37 UTC (rev 328) @@ -135,6 +135,10 @@ reason: The reason why the user could not log in: * "bad login" - The username and/or password is wrong. + * "login not allowed" - You may not log in right now + * "bot limit reached" - There're to much bots logged + in for your account + * "login blocked" - You (or your IP) are blocked """ pass Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-19 20:59:27 UTC (rev 327) +++ trunk/client/core/parser.py 2009-12-19 20:59:37 UTC (rev 328) @@ -62,23 +62,32 @@ def login(self, msg): """ - Called as answer for our login - * succeed - If we logged in - - if succeed is False: - * reason - Why we didn't log in - "bad login" - Username/password incorrect - - if succeed is True: + Called when we can log in * username - The username (with right caps) * cid - The server's connection-id """ - if msg['succeed'] == True: - self.client.username = msg['username'] - self.client.cid = msg['cid'] - - self.callback.logged_in(msg['username'], msg['cid']) + self.client.username = msg['username'] + self.client.cid = msg['cid'] - else: - self.callback.failed_logging_in(msg['reason']) + self.callback.logged_in(msg['username'], msg['cid']) + + def error(self, msg): + """ + Called when we did something wrong! + * reason - What we did wrong + "bad login" - Username/password wrong + "login not allowed" - User may not log in right now + "bot limit reached" - Too much bots logged in for this user + "login blocked" - The user (or IP) is blocked + """ + + if msg['reason'] == "bad login": + self.callback.failed_logging_in("bad login") + elif msg['reason'] == "login not allowed": + self.callback.failed_logging_in("login not allowed") + elif msg['reason'] == "bot limit reached": + self.callback.failed_logging_in("bot limit reached") + elif msg['reason'] == "login blocked": + self.callback.failed_logging_in("login blocked") + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-19 20:59:38
|
Revision: 327 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=327&view=rev Author: BlueWolf_ Date: 2009-12-19 20:59:27 +0000 (Sat, 19 Dec 2009) Log Message: ----------- A login-error will now go through {"error": {...}} instead of login. Added a callback so the app can now block users or bots Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-18 19:33:06 UTC (rev 326) +++ trunk/server/core/callback.py 2009-12-19 20:59:27 UTC (rev 327) @@ -153,11 +153,11 @@ def check_login(self, usr, pwd): """ This is used to verify the user's login. Return the real - username* when it's correct and False else if it's not. This function will - return False by default. + username* when it's correct and False when it's not. This + function will return False by default. * This is in case you decide to not make the username case - intensive + intensive. Now the clients knows the real username usr: The username @@ -169,3 +169,32 @@ """ return False + + def may_login(self, username, ip, bot, bots): + """ + This is called after (a succesfull) callback.check_login. Use + this to block certain users or IP's or block an additional bot + when the users reached the limit of connected bots. + + Return True when the user (or bot) may log in and False when it + may not. This will send an "not allowed" error. If you wish to + send an other error, return the error. An example may be: + * "login not allowed" + * "bot limit reached" + * "login blocked" + This function will return True by default. + + username: + The username for this user + ip: + The IP-adress this user has + bot: + If it wants to log in as a bot (True/False) + bots: + List with the cids of all active active bots for this + user. + + This is a placeholder. If you want to catch this event, + overwrite this in your own callback. + """ + return True Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-18 19:33:06 UTC (rev 326) +++ trunk/server/core/parser.py 2009-12-19 20:59:27 UTC (rev 327) @@ -60,7 +60,7 @@ # Decrypt the password pwd = rsa.decrypt(msg['pwd'], client['rsa']) - # Double sha, so n one can insert a "raw" sha + # Double sha, so no one can insert "raw" sha pwd = hashlib.sha1(pwd).hexdigest() if msg['for'] == 'VP': @@ -70,13 +70,34 @@ if username == False: # No login, bad user! - data.send("login", { - "succeed": False, - "reason": "bad login" - }) - return; + data.send("error", {"reason":"bad login"}) + return + + + + # Find out how many bots this user has running + bots = [] + for id, client in self.clients.items(): + if client['status'] == 'VP' and \ + client['user'] == username and \ + client['bot'] == True: + bots.append(id) + + # Check if the user may log in + may_login = self.callback.may_login(username, + data.get_ip(), msg['bot'], bots) + + if may_login != True: # User may not login + if may_login == False: + reason = "login not allowed" + else: + reason = may_login + data.send("error", {"reason":reason}) + return + + if msg['bot'] == False: # Just an ordinary user # Check if the user is already logged in @@ -96,10 +117,10 @@ client['status'] = "VP" data.send("login", { - "succeed": True, "username": username, "cid": cid }) else: # Client is bot pass #TODO + Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-18 19:33:06 UTC (rev 326) +++ trunk/server/core/server.py 2009-12-19 20:59:27 UTC (rev 327) @@ -331,6 +331,13 @@ try: self.__sock.send(data + chr(1)) except: pass + + def get_ip(self): + """ + Get the client's IP-adress + """ + + return self.__sock.getpeername()[0] class ConnectionError(Exception): pass This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-18 19:33:14
|
Revision: 326 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=326&view=rev Author: BlueWolf_ Date: 2009-12-18 19:33:06 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Changing the documentation regarding server disconnects Modified Paths: -------------- trunk/client/core/callback.py trunk/client/core/parser.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-18 19:32:18 UTC (rev 325) +++ trunk/client/core/callback.py 2009-12-18 19:33:06 UTC (rev 326) @@ -66,11 +66,13 @@ * "crash" - A crash happened on the server. Unless you sent malicious data or you're not admin of the server, there's nothing - you can do about it. + you can do about it + * "gone offline"- The server has just gone offline Return True if you want it to reconnect. To avoid unexpected - behaviour, you should *only* do this when reason is "closed"! + behaviour, you should *only* do this when reason is "closed" or + "gone offline"! If you provided the login in client.connect, it logs in automatically. Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-18 19:32:18 UTC (rev 325) +++ trunk/client/core/parser.py 2009-12-18 19:33:06 UTC (rev 326) @@ -36,10 +36,12 @@ def disconnect(self, msg): """ Called just before the server closes our connection. - * reason - Why the server disconnected us - "full" - Because the server is full - "duplicate" - Because another client has logged in - on the same account + * reason - Why the server disconnected us + "full" - Because the server is full + "duplicate" - Because another client has logged in + on the same account + "crash" - The server-parser has made a crash + "gone offline" - Server has just shutdown """ # reason - Why it's going to disconnect us This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-18 19:32:28
|
Revision: 325 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=325&view=rev Author: BlueWolf_ Date: 2009-12-18 19:32:18 +0000 (Fri, 18 Dec 2009) Log Message: ----------- You can now close the server with server.close. This will disconnect all users after sending the reason Modified Paths: -------------- trunk/server/core/server.py Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-18 19:07:07 UTC (rev 324) +++ trunk/server/core/server.py 2009-12-18 19:32:18 UTC (rev 325) @@ -130,7 +130,10 @@ #Infinite loop that will wait for incomming connections while 1: - sock, addr = self.__sock.accept() + try: sock, addr = self.__sock.accept() + except: + # Server has gone offline + return if self.__config['max_connections'] and \ len(self.clients) >= \ @@ -173,9 +176,26 @@ continue self.clients[cid]['con'].start() - + + def exit(self, reason = "gone offline"): + """ + Will shutdown the server and sends an 'gone offline' message to + all connected clients and then closes their connection. Use this + when you're about to shutdown your server. + This will call the callback.connection_closed for all clients! + Note that you'll not be able to start the server again + """ + try: self.__sock.shutdown(0) + except: pass + self.__sock.close() + self.__sock = None + for client in self.clients.values(): + client['con'].close_msg(reason) + + + class Client(threading.Thread): """ This class manages the socket for the connection to clients. @@ -262,15 +282,12 @@ self.__parser_crash() # Kick connection - self.send("disconnect", - {"reason":"crash"}) - self.close("crash") + self.close_msg("crash") def close(self, reason = "manual"): """ Closes the connection for this client and kills the socket. """ - if self.is_online == False: return self.is_online = False @@ -281,7 +298,26 @@ self.__call.connection_closed(self.__cid, reason) del self.__clients[self.__cid] + + def close_msg(self, reason): + """ + Same as Client.close, but this will send the reason to the + client before closing + """ + if self.is_online == False: return + self.is_online = False + + self.send("disconnect", {"reason": reason}) + + try: self.__sock.shutdown(0); + except: pass + self.__sock.close() + + self.__call.connection_closed(self.__cid, reason) + del self.__clients[self.__cid] + + def send(self, data_header, data_body = {}): """ Sends `data_body` of type `data_header` to the client. It will This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-18 19:07:17
|
Revision: 324 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=324&view=rev Author: BlueWolf_ Date: 2009-12-18 19:07:07 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Uses hashlib.sha1 instead of md5 for the cids Modified Paths: -------------- trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-18 19:06:56 UTC (rev 323) +++ trunk/server/core/parser.py 2009-12-18 19:07:07 UTC (rev 324) @@ -15,7 +15,7 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -import rsa, sha +import rsa, hashlib class Parser(): def __init__(self, callback, server): @@ -60,8 +60,8 @@ # Decrypt the password pwd = rsa.decrypt(msg['pwd'], client['rsa']) - # Double sha, so one can not insert "raw" sha - pwd = sha.new(pwd).hexdigest() + # Double sha, so n one can insert a "raw" sha + pwd = hashlib.sha1(pwd).hexdigest() if msg['for'] == 'VP': Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-18 19:06:56 UTC (rev 323) +++ trunk/server/core/server.py 2009-12-18 19:07:07 UTC (rev 324) @@ -15,7 +15,7 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -import simplejson, socket, threading, time, md5, random, sys +import simplejson, socket, threading, time, random, sys, hashlib from parser import Parser import rsa @@ -154,9 +154,9 @@ continue # Create unique client-id - cid = md5.new(addr[0] + str(addr[1])).hexdigest()[:8] + cid = hashlib.sha1(addr[0] + str(addr[1])).hexdigest()[:8] while cid in self.clients: - cid = md5.new(addr[0] + str(addr[1]) + + cid = hashlib.sha1(addr[0] + str(addr[1]) + str(random.random())).hexdigest()[:8] self.clients[cid] = { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-18 19:07:12
|
Revision: 323 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=323&view=rev Author: BlueWolf_ Date: 2009-12-18 19:06:56 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Changed sha to hashlib.sha1 in the documentation Modified Paths: -------------- trunk/client/core/client.py Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2009-12-18 10:37:06 UTC (rev 322) +++ trunk/client/core/client.py 2009-12-18 19:06:56 UTC (rev 323) @@ -178,11 +178,11 @@ """ Log in to the server. You should have received the RSA-key before calling this! - pwd is expected to be sha. Bot should be True or False. Bots + pwd is expected to be sha1. Bot should be True or False. Bots have different functions in VP. - >>> import sha - >>> sha.new("MyVerySecretPassword").hexdigest() + >>> import hashlib + >>> hashlib.sha1("MyVerySecretPassword").hexdigest() 'dc2275e9f1e53926dce503ec7d9df9ac9ce07dfc' """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-18 10:37:14
|
Revision: 322 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=322&view=rev Author: BlueWolf_ Date: 2009-12-18 10:37:06 +0000 (Fri, 18 Dec 2009) Log Message: ----------- Added a small howto on how to use the VirtualCore in your program Modified Paths: -------------- trunk/client/core/__init__.py Modified: trunk/client/core/__init__.py =================================================================== --- trunk/client/core/__init__.py 2009-12-16 20:26:33 UTC (rev 321) +++ trunk/client/core/__init__.py 2009-12-18 10:37:06 UTC (rev 322) @@ -1,8 +1,3 @@ -""" -This is the client core for Virtual Playground. -You can use this by importing Client and Callback -""" - ## This file is part of Virtual Playground ## Copyright (c) 2009 Jos Ratsma + Koen Koning @@ -20,6 +15,47 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" +If you want to build your own Virtual Playground client and are using Python, +you can import this core for ready-to-use functions for the client. The core +will do all the work for you. The only thing you have to do is to make the user +interface. + +An example on how to connect to the server (as far it has been made): +>>> from core import Client, Callback +>>> import sha + +The Client is the core. You need to create your own callback class, which should +inherit the functions of Callback. You get feedback from your callback-class. +>>> class callback(Callback): +... def connect(self): +... print "Connect!" +... def logged_in(self, username, uid, cid): + print "I'm logged in!" + +Because it inherits all the (empty) functions from Callback, you don't have to +create all the functions yourself. Also, in help(Callback) you see all possible +callbacks along with their description. + +The next thing is the config. See help(Client) for all the available +config-options. +>>> config = { +... "app_name": "My awesome application", +... "app_version": "2.0" +... } + +Start the core. +>>> client = Client(config, callback()) +>>> client.connect("my.host.com", 1234, "Username", \ +... sha.new("Password").hexdigest()) + +If you have the server online and your login is correct, it will print: +Connect! +I'm now logged in! + +Congratulation! You have just made your first connection to your server! +""" + import client, callback Client = client.Client Callback = callback.Callback This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-16 20:26:40
|
Revision: 321 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=321&view=rev Author: BlueWolf_ Date: 2009-12-16 20:26:33 +0000 (Wed, 16 Dec 2009) Log Message: ----------- No id/uid will be send by the server anymore Modified Paths: -------------- trunk/client/core/callback.py trunk/client/core/client.py trunk/client/core/parser.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-16 20:26:07 UTC (rev 320) +++ trunk/client/core/callback.py 2009-12-16 20:26:33 UTC (rev 321) @@ -107,7 +107,7 @@ """ pass - def logged_in(self, username, uid, cid): + def logged_in(self, username, cid): """ Called when we are logged in. @@ -115,9 +115,6 @@ The username for this user. Use this, instead what the user typed in, because this has the right caps. Also available in client.username. - uid: - The unique user-id for this user. Also available in - client.uid cid: The unique client-id for this connection. Also available in client.cid Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2009-12-16 20:26:07 UTC (rev 320) +++ trunk/client/core/client.py 2009-12-16 20:26:33 UTC (rev 321) @@ -82,7 +82,6 @@ # Info after logging in self.username = None - self.uid = None # User-id self.cid = None # Connection-id threading.Thread.__init__(self) @@ -260,7 +259,6 @@ self.__rsa = None self.username = None self.cid = None - self.uid = None try: self.__sock.shutdown(0) except: pass Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-16 20:26:07 UTC (rev 320) +++ trunk/client/core/parser.py 2009-12-16 20:26:33 UTC (rev 321) @@ -69,17 +69,14 @@ if succeed is True: * username - The username (with right caps) - * id - The user-id - * uid - The server's connection-id + * cid - The server's connection-id """ if msg['succeed'] == True: self.client.username = msg['username'] - self.client.uid = msg['uid'] self.client.cid = msg['cid'] - self.callback.logged_in(msg['username'], msg['uid'], - msg['cid']) + self.callback.logged_in(msg['username'], msg['cid']) else: self.callback.failed_logging_in(msg['reason']) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-16 20:26:15
|
Revision: 320 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=320&view=rev Author: BlueWolf_ Date: 2009-12-16 20:26:07 +0000 (Wed, 16 Dec 2009) Log Message: ----------- Forgot it's up to the app to check for logins, not the core Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/parser.py trunk/server/core/server.py Removed Paths: ------------- trunk/server/core/database.py trunk/server/core/database.sql Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-15 21:01:38 UTC (rev 319) +++ trunk/server/core/callback.py 2009-12-16 20:26:07 UTC (rev 320) @@ -147,4 +147,25 @@ This is a placeholder. If you want to catch this event, overwrite this in your own callback. """ - + + pass + + def check_login(self, usr, pwd): + """ + This is used to verify the user's login. Return the real + username* when it's correct and False else if it's not. This function will + return False by default. + + * This is in case you decide to not make the username case + intensive + + usr: + The username + pwd: + The (double) sha-ed password + + This is a placeholder. If you want to catch this event, + overwrite this in your own callback. + """ + + return False Deleted: trunk/server/core/database.py =================================================================== --- trunk/server/core/database.py 2009-12-15 21:01:38 UTC (rev 319) +++ trunk/server/core/database.py 2009-12-16 20:26:07 UTC (rev 320) @@ -1,72 +0,0 @@ -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program;guaranteed if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import MySQLdb - -class Database(): - def __init__(self, host, user, passwd, db): - self.host = host - self.user = user - self.passwd = passwd - self.db = db - - def connect(self): - try: - self.dbcon = MySQLdb.Connect( - host = self.host, - user = self.user, - passwd = self.passwd, - db = self.db - ) - self.db = self.dbcon.cursor(MySQLdb.cursors.DictCursor) - - except: - raise DBConnectionError( - "Could not connect to the database!") - - def __getattr__(self, attr): - """ - Called when a functions is not in our class. We pass everything - through to our MySQLdb - """ - - def exc(*arg): - """ - Will return the real function from MySQLdb. Will ping - before executing the command, so it will automatically - reconnect. - """ - - # Uncomment for raw mysql-debugging! Fun guaranteed! - # print '\tMySQLdb.' + attr + repr(arg) - - func = getattr(self.db, attr) - try: - dbfunc = func(*arg) - except MySQLdb.OperationalError, message: - if message[0] == 2006: # Mysql has gone away - self._connect() - dbfunc = func(*arg) - else: #Some other error we don't care about - raise MySQLdb.OperationalError, message - - return dbfunc - - return exc - -class DBConnectionError(Exception): - pass Deleted: trunk/server/core/database.sql =================================================================== --- trunk/server/core/database.sql 2009-12-15 21:01:38 UTC (rev 319) +++ trunk/server/core/database.sql 2009-12-16 20:26:07 UTC (rev 320) @@ -1,10 +0,0 @@ --- --- Table structure for table `users` --- - -CREATE TABLE IF NOT EXISTS `users` ( - `id` int(11) NOT NULL auto_increment COMMENT 'Also known as uid', - `username` varchar(255) NOT NULL, - `password` varchar(40) NOT NULL COMMENT 'Password in double sha1', - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1; Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-15 21:01:38 UTC (rev 319) +++ trunk/server/core/parser.py 2009-12-16 20:26:07 UTC (rev 320) @@ -26,7 +26,6 @@ self.server = server # Shortkeys - self.db = self.server.database self.clients = self.server.clients def __call__(self, cid, msg): @@ -66,18 +65,11 @@ if msg['for'] == 'VP': - self.db.execute(""" - SELECT - id, username, password - FROM - users - WHERE - username = %(user)s - AND password = %(password)s - """, {'user': msg['usr'], 'password': pwd}) - user = self.db.fetchone() + # Check for login + username = self.callback.check_login(msg['usr'], pwd) - if user == None: # No login, bad user! + if username == False: + # No login, bad user! data.send("login", { "succeed": False, "reason": "bad login" @@ -91,7 +83,7 @@ for id, cl in self.clients.items(): if cl['status'] == "VP" and \ cl['bot'] == False and \ - cl['id'] == user['id']: + cl['user'] == username: # Disconnect user cl['con'].send("disconnect", {'reason':'duplicate'}) @@ -99,16 +91,14 @@ # Log the user in - client['id'] = user['id'] - client['user'] = user['username'] + client['user'] = username client['bot'] = False client['status'] = "VP" data.send("login", { "succeed": True, - "username": user['username'], - "cid": cid, - "uid": user['id'] + "username": username, + "cid": cid }) else: # Client is bot Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-15 21:01:38 UTC (rev 319) +++ trunk/server/core/server.py 2009-12-16 20:26:07 UTC (rev 320) @@ -17,7 +17,6 @@ import simplejson, socket, threading, time, md5, random, sys from parser import Parser -from database import Database import rsa class Server(threading.Thread): @@ -69,9 +68,8 @@ """ - def __init__(self, config, callback_class, database): + def __init__(self, config, callback_class): self.__sock = None - self.database = Database(**database) self.__call = callback_class # Create all default settings @@ -103,9 +101,6 @@ if self.__sock: raise ConnectionError("The server is already online!") - # Connect to the database - self.database.connect() - #Load our server socket self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <blu...@us...> - 2009-12-15 21:01:48
|
Revision: 319 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=319&view=rev Author: bluewolf_ Date: 2009-12-15 21:01:38 +0000 (Tue, 15 Dec 2009) Log Message: ----------- Removed files that weren't part of the project and where not updated anyway Removed Paths: ------------- trunk/client/VP.py trunk/client/callback.py trunk/server/VPS.py trunk/server/callback.py Deleted: trunk/client/VP.py =================================================================== --- trunk/client/VP.py 2009-12-15 20:57:44 UTC (rev 318) +++ trunk/client/VP.py 2009-12-15 21:01:38 UTC (rev 319) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import time -import core -from callback import Callback - -def main(): - client = core.Client(Callback) - client.connect('localhost', 5162) - - while 1: - #Pygame event loop should go here later. - try: - time.sleep(5000) - except KeyboardInterrupt: - print - exit() - -if __name__ == '__main__': main() - Deleted: trunk/client/callback.py =================================================================== --- trunk/client/callback.py 2009-12-15 20:57:44 UTC (rev 318) +++ trunk/client/callback.py 2009-12-15 21:01:38 UTC (rev 319) @@ -1,35 +0,0 @@ -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import core - -class Callback(core.Callback): - def connected(self, con): - print 'Connected!' - self.con = con - con.send('HI') - - def disconnected(self): - print 'CONNECTION DEAD' - #return True #reconnect - - def data_received(self, data): - print '<', repr(data) - - def data_send(self, data): - print '>', repr(data) - Deleted: trunk/server/VPS.py =================================================================== --- trunk/server/VPS.py 2009-12-15 20:57:44 UTC (rev 318) +++ trunk/server/VPS.py 2009-12-15 21:01:38 UTC (rev 319) @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import time -import core -from callback import Callback - -def main(): - conf = {'host':'', - 'port':5162, - 'max_connections':0} - - server = core.Server(conf, Callback) - server.start_server() - - while 1: - try: - time.sleep(5000) - except KeyboardInterrupt: - print - exit() - -if __name__ == '__main__': main() - Deleted: trunk/server/callback.py =================================================================== --- trunk/server/callback.py 2009-12-15 20:57:44 UTC (rev 318) +++ trunk/server/callback.py 2009-12-15 21:01:38 UTC (rev 319) @@ -1,49 +0,0 @@ -## This file is part of Virtual Playground -## Copyright (c) 2009 Jos Ratsma + Koen Koning - -## This program is free software; you can redistribute it and/or -## modify it under the terms of the GNU General Public License -## as published by the Free Software Foundation; either -## version 2 of the License, or (at your option) any later version. - -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. - -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import core - -class Callback(core.Callback): - def server_online(self, clients): - print 'Server is online! :-)' - self.clients = clients - - def connection_opened(self, uid, client, host, port): - print 'New connection:', uid - - def connection_close(self, uid): - print 'Connection dead:', uid - - def connection_limit_exceeded(self, ip, current_connections, - max_connections): - print 'Connection limit exceeded: used %s/%s slots, auto '\ - 'accepted request from %s.' % (current_connections, - max_connections, ip) - return True - - def data_send(self, uid, data): - print '>', repr(data) - - def data_received(self, uid, data): - print '<', repr(data) - - head = data.keys()[0] - body = data[head] - - if head == 'HI': - self.clients[uid].send('LO') - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-15 20:57:51
|
Revision: 318 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=318&view=rev Author: BlueWolf_ Date: 2009-12-15 20:57:44 +0000 (Tue, 15 Dec 2009) Log Message: ----------- Updated doc in callback regarding disconnects due to server crashes Modified Paths: -------------- trunk/client/core/callback.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-15 20:56:45 UTC (rev 317) +++ trunk/client/core/callback.py 2009-12-15 20:57:44 UTC (rev 318) @@ -63,6 +63,10 @@ * "duplicate" - Another client has logged in on this account. This connection has been kicked + * "crash" - A crash happened on the server. Unless + you sent malicious data or you're not + admin of the server, there's nothing + you can do about it. Return True if you want it to reconnect. To avoid unexpected This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-15 20:56:55
|
Revision: 317 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=317&view=rev Author: BlueWolf_ Date: 2009-12-15 20:56:45 +0000 (Tue, 15 Dec 2009) Log Message: ----------- It now catches crashes from the parser Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-14 21:23:28 UTC (rev 316) +++ trunk/server/core/callback.py 2009-12-15 20:56:45 UTC (rev 317) @@ -69,6 +69,10 @@ * "duplicate" - Another client has logged in on this account. This connection has been kicked + * "crash" - A crash in the parser happened. This + could be because of malicious data was + send. Use callback.debug_traceback to + get the precise error This is a placeholder. If you want to catch this event, @@ -128,4 +132,19 @@ overwrite this in your own callback. """ pass + + + def debug_crash(self, tracback): + """ + Usefull for debugging. Normally, when the parser (our any code + in the parser, like the callback) crashes, the connection will + be kicked and the error will be ignored. If you want to know the + precise error, use this function to get the trackback + + traceback: + A string... with a traceback, duh :-) + + This is a placeholder. If you want to catch this event, + overwrite this in your own callback. + """ Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-14 21:23:28 UTC (rev 316) +++ trunk/server/core/server.py 2009-12-15 20:56:45 UTC (rev 317) @@ -15,7 +15,7 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -import simplejson, socket, threading, time, md5, random +import simplejson, socket, threading, time, md5, random, sys from parser import Parser from database import Database import rsa @@ -201,7 +201,25 @@ def __repr__(self): return '<Client(%s)>'%self.__cid + + + def __parser_crash(self): + """ + Will create a traceback and call the callback + """ + #Get some debugging info + et, ev, tb = sys.exc_info() + traceback = "" + while tb: + co = tb.tb_frame.f_code + traceback += str(co.co_filename) + ':' + \ + str(tb.tb_lineno) + '\n' + tb = tb.tb_next + traceback += ev.__class__.__name__ + ': ' + str(ev) + + self.__call.debug_crash(traceback) + def run(self): """ Used by threading, not for external usage. @@ -242,8 +260,16 @@ data = data[:-1] for msg in data: - self.__parser(self.__cid, + try: + self.__parser(self.__cid, simplejson.loads(msg)) + except Exception, Er: + self.__parser_crash() + + # Kick connection + self.send("disconnect", + {"reason":"crash"}) + self.close("crash") def close(self, reason = "manual"): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-14 21:23:36
|
Revision: 316 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=316&view=rev Author: BlueWolf_ Date: 2009-12-14 21:23:28 +0000 (Mon, 14 Dec 2009) Log Message: ----------- Server now sends uid(user-id) and cid(connection-id) Modified Paths: -------------- trunk/client/core/client.py trunk/client/core/parser.py Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2009-12-14 21:22:34 UTC (rev 315) +++ trunk/client/core/client.py 2009-12-14 21:23:28 UTC (rev 316) @@ -47,9 +47,9 @@ The settings: -app_name The name of your program. Will be send when logging in. - -app_name - The version of your program. Will be send when logging. - Should be a string. + -app_version + The version of your program. Will be send when logging + in. Should be a string. """ def __init__(self, config, callback_class): Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-14 21:22:34 UTC (rev 315) +++ trunk/client/core/parser.py 2009-12-14 21:23:28 UTC (rev 316) @@ -75,11 +75,11 @@ if msg['succeed'] == True: self.client.username = msg['username'] - self.client.uid = msg['id'] - self.client.cid = msg['uid'] + self.client.uid = msg['uid'] + self.client.cid = msg['cid'] - self.callback.logged_in(msg['username'], msg['id'], - msg['uid']) + self.callback.logged_in(msg['username'], msg['uid'], + msg['cid']) else: self.callback.failed_logging_in(msg['reason']) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-14 21:22:43
|
Revision: 315 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=315&view=rev Author: BlueWolf_ Date: 2009-12-14 21:22:34 +0000 (Mon, 14 Dec 2009) Log Message: ----------- Changed uid(and id) now means the user-id and cid now means connection/client-id. Fixing some minor things. Server does another sha for the password Modified Paths: -------------- trunk/server/core/callback.py trunk/server/core/database.sql trunk/server/core/parser.py trunk/server/core/server.py Modified: trunk/server/core/callback.py =================================================================== --- trunk/server/core/callback.py 2009-12-14 19:48:14 UTC (rev 314) +++ trunk/server/core/callback.py 2009-12-14 21:22:34 UTC (rev 315) @@ -29,15 +29,14 @@ """ pass - def connection_opened(self, uid, client, host, port): + def connection_opened(self, cid, client, host, port): """ Called when a new client connects and we accepted the connection. You can, however, still kill the connection by returning True in this function. - uid: - The unique ID for this connection. This is usually - 'ip:port'. + cid: + The unique ID for this connection (connection-id). client: The client info. Normally, you don't need this. host: @@ -51,15 +50,14 @@ """ pass - def connection_closed(self, uid, reason): + def connection_closed(self, cid, reason): """ Called when a connection with a client is closed. This can be when they closed their connection themselves, or when we disconnect someone. - uid: - The unique ID for this connection. This is usually - 'ip:port'. + cid: + The unique ID for this connection (connection-id). reason: A string, representing the reason why it disconnected. It currently has these options: @@ -101,14 +99,13 @@ """ pass - def data_received(self, uid, data): + def data_received(self, cid, data): """ Called when the server received data from one of the clients. Normally, you don't need this. - uid: - The unique ID for this connection. This is usually - 'ip:port'. + cid: + The unique ID for this connection (connection-id) This is a placeholder. If you want to catch this event, @@ -116,14 +113,13 @@ """ pass - def data_send(self, uid, data): + def data_send(self, cid, data): """ Called when the server send data to one of the clients. Normally, you don't need this. - uid: - The unique ID for this connection. This is usually - 'ip:port'. + cid: + The unique ID for this connection. (connectin-id) data: Dict with the data that will be send. Modified: trunk/server/core/database.sql =================================================================== --- trunk/server/core/database.sql 2009-12-14 19:48:14 UTC (rev 314) +++ trunk/server/core/database.sql 2009-12-14 21:22:34 UTC (rev 315) @@ -1,33 +1,10 @@ --- phpMyAdmin SQL Dump --- version 3.1.2deb1ubuntu0.2 --- http://www.phpmyadmin.net -- --- Host: localhost --- Generation Time: Dec 14, 2009 at 08:38 PM --- Server version: 5.0.75 --- PHP Version: 5.2.6-3ubuntu4.4 - -SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; - - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; - --- --- Database: `VP` --- - --- -------------------------------------------------------- - --- -- Table structure for table `users` -- CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL auto_increment COMMENT 'Also known as uid', `username` varchar(255) NOT NULL, - `password` varchar(40) NOT NULL COMMENT 'Password in sha1', + `password` varchar(40) NOT NULL COMMENT 'Password in double sha1', PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ; +) ENGINE=MyISAM DEFAULT CHARSET=latin1; Modified: trunk/server/core/parser.py =================================================================== --- trunk/server/core/parser.py 2009-12-14 19:48:14 UTC (rev 314) +++ trunk/server/core/parser.py 2009-12-14 21:22:34 UTC (rev 315) @@ -15,7 +15,7 @@ ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -import rsa +import rsa, sha class Parser(): def __init__(self, callback, server): @@ -29,18 +29,18 @@ self.db = self.server.database self.clients = self.server.clients - def __call__(self, uid, msg): - self.callback.data_received(uid, msg) + def __call__(self, cid, msg): + self.callback.data_received(cid, msg) head = msg.keys()[0] body = msg[head] func = getattr(self, str(head), None) if (func): - func(uid, body) + func(cid, body) - def login(self, uid, msg): + def login(self, cid, msg): """ Send when the users wants to log in usr - The username @@ -51,7 +51,7 @@ client - list with [app_name, app_version] """ - client = self.clients[uid] + client = self.clients[cid] data = client['con'] # Ignore when user is already logged in @@ -61,6 +61,9 @@ # Decrypt the password pwd = rsa.decrypt(msg['pwd'], client['rsa']) + # Double sha, so one can not insert "raw" sha + pwd = sha.new(pwd).hexdigest() + if msg['for'] == 'VP': self.db.execute(""" @@ -104,8 +107,8 @@ data.send("login", { "succeed": True, "username": user['username'], - "uid": uid, - "id": user['id'] + "cid": cid, + "uid": user['id'] }) else: # Client is bot Modified: trunk/server/core/server.py =================================================================== --- trunk/server/core/server.py 2009-12-14 19:48:14 UTC (rev 314) +++ trunk/server/core/server.py 2009-12-14 21:22:34 UTC (rev 315) @@ -158,26 +158,26 @@ sock.close() continue - # Create unique user-id - uid = md5.new(addr[0] + str(addr[1])).hexdigest()[:8] - while uid in self.clients: - uid = md5.new(addr[0] + str(addr[1]) + + # Create unique client-id + cid = md5.new(addr[0] + str(addr[1])).hexdigest()[:8] + while cid in self.clients: + cid = md5.new(addr[0] + str(addr[1]) + str(random.random())).hexdigest()[:8] - self.clients[uid] = { + self.clients[cid] = { "status": "", - "con": Client(uid, sock, self.clients, + "con": Client(cid, sock, self.clients, self.__call, self.__parse, self.__config) } - if self.__call.connection_opened(uid, - self.clients[uid], *addr): + if self.__call.connection_opened(cid, + self.clients[cid], *addr): #User returned True -> drop user - self.clients[uid]['con'].close() + self.clients[cid]['con'].close() continue - self.clients[uid]['con'].start() + self.clients[cid]['con'].start() @@ -187,9 +187,9 @@ Each client has it's own class. """ - def __init__(self, uid, sock, clients, callback, parser, config): + def __init__(self, cid, sock, clients, callback, parser, config): - self.__uid = uid + self.__cid = cid self.__sock = sock self.__clients = clients self.__call = callback @@ -200,7 +200,7 @@ threading.Thread.__init__(self) def __repr__(self): - return '<Client(%s)>'%self.__uid + return '<Client(%s)>'%self.__cid def run(self): """ @@ -215,7 +215,7 @@ # rsa-key public, private = rsa.gen_pubpriv_keys( self.__config['rsa_bits']) - self.__clients[self.__uid]['rsa'] = private + self.__clients[self.__cid]['rsa'] = private self.send("rsa", {"public": public}) @@ -242,7 +242,7 @@ data = data[:-1] for msg in data: - self.__parser(self.__uid, + self.__parser(self.__cid, simplejson.loads(msg)) def close(self, reason = "manual"): @@ -257,9 +257,9 @@ except: pass self.__sock.close() - self.__call.connection_closed(self.__uid, reason) + self.__call.connection_closed(self.__cid, reason) - del self.__clients[self.__uid] + del self.__clients[self.__cid] def send(self, data_header, data_body = {}): """ @@ -268,7 +268,7 @@ (chr(1)) will be send automatically too. `data_header` is a string, `data_body` a dict. """ - self.__call.data_send(self.__uid, {data_header:data_body}) + self.__call.data_send(self.__cid, {data_header:data_body}) data = simplejson.dumps({data_header:data_body}) try: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <Blu...@us...> - 2009-12-14 19:48:21
|
Revision: 314 http://virtplayground.svn.sourceforge.net/virtplayground/?rev=314&view=rev Author: BlueWolf_ Date: 2009-12-14 19:48:14 +0000 (Mon, 14 Dec 2009) Log Message: ----------- Client can now log it to the server. It uses RSA to protect the password Modified Paths: -------------- trunk/client/core/callback.py trunk/client/core/client.py trunk/client/core/parser.py Added Paths: ----------- trunk/client/core/rsa.py Modified: trunk/client/core/callback.py =================================================================== --- trunk/client/core/callback.py 2009-12-14 19:46:21 UTC (rev 313) +++ trunk/client/core/callback.py 2009-12-14 19:48:14 UTC (rev 314) @@ -18,15 +18,33 @@ class Callback: def connected(self): """ - This will be called when a connection with the server is made. - `con` contains the core's `Client` class, so you can use it to - send stuff. + When the connection is made, this will be called. Note that the + server can still kick the connection because it is full. You + should not client.login here, because the server still has to + send a rsa-key. See callback.received_rsa for this event. - This is placeholder. If you want to catch this event, + This is a placeholder. If you want to catch this event, overwrite this in your own callback """ pass + + def received_rsa(self, public): + """ + When a connection is made, the server will generate a rsa-key. + The client has to wait for this, before logging in. After this + event, you can use client.login (if you haven't specified a + login at client.connect) + public: + The generated public rsa-key. It is a dict with 2 + values: e and n. It's used for encoding the password + + + This is a placeholder. If you want to catch this event, + overwrite this in your own callback + """ + pass + def disconnected(self, reason): """ This is called when the connection dies. For example when you @@ -42,12 +60,17 @@ - more connections * "manual" - Client (you) closed the connection with .close() + * "duplicate" - Another client has logged in on this + account. This connection has been + kicked - Return True if you want it to reconnect. Be warned that this - event also will be raised when you do .close()! + Return True if you want it to reconnect. To avoid unexpected + behaviour, you should *only* do this when reason is "closed"! + If you provided the login in client.connect, it logs in + automatically. - This is placeholder. If you want to catch this event, + This is a placeholder. If you want to catch this event, overwrite this in your own callback """ pass @@ -61,7 +84,7 @@ Dict with the data that has been received - This is placeholder. If you want to catch this event, + This is a placeholder. If you want to catch this event, overwrite this in your own callback """ pass @@ -75,8 +98,40 @@ Dict with the data that will be send. - This is placeholder. If you want to catch this event, + This is a placeholder. If you want to catch this event, overwrite this in your own callback """ pass - + + def logged_in(self, username, uid, cid): + """ + Called when we are logged in. + + username: + The username for this user. Use this, instead what the + user typed in, because this has the right caps. Also + available in client.username. + uid: + The unique user-id for this user. Also available in + client.uid + cid: + The unique client-id for this connection. Also available + in client.cid + + This is a placeholder. If you want to catch this event, + overwrite this in your own callback + """ + + pass + + def failed_logging_in(self, reason): + """ + This happens when the user could not log in. You can safely use + client.login again. + + reason: + The reason why the user could not log in: + * "bad login" - The username and/or password is wrong. + """ + + pass Modified: trunk/client/core/client.py =================================================================== --- trunk/client/core/client.py 2009-12-14 19:46:21 UTC (rev 313) +++ trunk/client/core/client.py 2009-12-14 19:48:14 UTC (rev 314) @@ -17,7 +17,12 @@ import simplejson, socket, threading, time from parser import Parser +import rsa +__name__ = "VirtualCore" +__version__ = "0.0.1" + + class Client(threading.Thread): """ This is the client-core for Virtual Playground. This will handle the @@ -40,9 +45,11 @@ -------- The settings: - Currently, the config is a bit lonely and quiet. Do you want to - fill it? :-( - + -app_name + The name of your program. Will be send when logging in. + -app_name + The version of your program. Will be send when logging. + Should be a string. """ def __init__(self, config, callback_class): @@ -55,12 +62,29 @@ self.__config_default(config) self.__config = config + # Class that parsers all incomming data self.__parse = Parser(self.__call, self) + + # So the client knows if we are online. Is used for + # disconnecting self.__is_online = False + # Treading-event so that run() waits before it's allowed to + # connect self.__do_connect = threading.Event() self.__do_connect.clear() + # Auto log in after connecting. Used by self.connect + self.__login_after_connecting = () + + # RSA + self.__rsakey = None + + # Info after logging in + self.username = None + self.uid = None # User-id + self.cid = None # Connection-id + threading.Thread.__init__(self) self.setDaemon(True) @@ -68,15 +92,18 @@ def __config_default(self, config): - #config.setdefault('whatever', 'duh') - pass + config.setdefault('app_name', __name__) + config.setdefault('app_version', __version__) - def connect(self, host, port): + def connect(self, host, port, usr = None, pwd = None, bot = False): """ Will (try to) connect to the server on `host`:`port`. If you want to reconnect, you have to close the connection first, if it's still open. + + You can optionally let it login automatically. See client.login + for the usr, pwd and bot variables. """ self.__host = host @@ -85,9 +112,16 @@ if self.__sock: raise ConnectionError("You are already connected!") - self.__do_connect.set() # Release the hounds + # Should we login after connecting? + if usr and pwd: + self.__login_after_connecting = (usr, pwd, bot) + else: + self.__login_after_connecting = (); + self.__do_connect.set() # Releasing the hounds + + def run(self): """ Used by threading, not for external usage. @@ -141,6 +175,47 @@ if self.__is_online: self.close(disconnect_reason) + def login(self, usr, pwd, bot = False): + """ + Log in to the server. You should have received the RSA-key + before calling this! + pwd is expected to be sha. Bot should be True or False. Bots + have different functions in VP. + + >>> import sha + >>> sha.new("MyVerySecretPassword").hexdigest() + 'dc2275e9f1e53926dce503ec7d9df9ac9ce07dfc' + """ + + if self.cid != None: + raise LoginError("You are already logged in") + + # Get our login + if not (usr and pwd): + if self.__login_after_connecting: + usr, pwd, bot = self.__login_after_connecting + else: + return # Just ignore it.. + + if not self.__sock: + raise ConnectionError("You are not connected!") + + if not self.__rsakey: + raise LoginError("No rsa-key available") + + # Convert pwd + pwd = rsa.encrypt(pwd, self.__rsakey) + + # Log in + self.send("login", { + "usr": usr, + "pwd": pwd, + "bot": bool(bot), + "for": "VP", + "client": ( self.__config['app_name'], + self.__config['app_version'] + ) + }) def send(self, data_header, data_body = {}): @@ -181,6 +256,12 @@ self.__pinger.cancel() self.__pinger = None + # Reset variables + self.__rsa = None + self.username = None + self.cid = None + self.uid = None + try: self.__sock.shutdown(0) except: pass self.__sock.close() @@ -223,3 +304,6 @@ class ConnectionError(Exception): pass + +class LoginError(Exception): + pass Modified: trunk/client/core/parser.py =================================================================== --- trunk/client/core/parser.py 2009-12-14 19:46:21 UTC (rev 313) +++ trunk/client/core/parser.py 2009-12-14 19:48:14 UTC (rev 314) @@ -18,14 +18,13 @@ class Parser(): def __init__(self, callback, client): """ - This class parses all received messages from client. - It may need a better name... + This class parses all received messages from the server. """ - self.__call = callback - self.__client = client + self.callback = callback + self.client = client def __call__(self, msg): - self.__call.data_received(msg) + self.callback.data_received(msg) head = msg.keys()[0] body = msg[head] @@ -36,9 +35,51 @@ def disconnect(self, msg): """ - Called just before the server closes our connection + Called just before the server closes our connection. + * reason - Why the server disconnected us + "full" - Because the server is full + "duplicate" - Because another client has logged in + on the same account """ # reason - Why it's going to disconnect us - if msg['reason'] == 'full': - self.__client.close("full") + self.client.close(msg["reason"]) + + def rsa(self, msg): + """ + Called when the connection has been made and a rsa-key has been + generated. We can now log in using this key + * public - The public rsa-key, which contains e & n + """ + + self.client._Client__rsakey = msg['public'] + + self.callback.received_rsa(msg['public']) + + self.client.login(None, None) + + def login(self, msg): + """ + Called as answer for our login + * succeed - If we logged in + + if succeed is False: + * reason - Why we didn't log in + "bad login" - Username/password incorrect + + if succeed is True: + * username - The username (with right caps) + * id - The user-id + * uid - The server's connection-id + """ + + if msg['succeed'] == True: + self.client.username = msg['username'] + self.client.uid = msg['id'] + self.client.cid = msg['uid'] + + self.callback.logged_in(msg['username'], msg['id'], + msg['uid']) + + else: + self.callback.failed_logging_in(msg['reason']) Added: trunk/client/core/rsa.py =================================================================== --- trunk/client/core/rsa.py (rev 0) +++ trunk/client/core/rsa.py 2009-12-14 19:48:14 UTC (rev 314) @@ -0,0 +1,427 @@ +"""RSA module + +Module for calculating large primes, and RSA encryption, decryption, +signing and verification. Includes generating public and private keys. +""" + +__author__ = "Sybren Stuvel, Marloes de Boer and Ivo Tamboer" +__date__ = "2009-01-22" + +# NOTE: Python's modulo can return negative numbers. We compensate for +# this behaviour using the abs() function + +from cPickle import dumps, loads +import base64 +import math +import os +import random +import sys +import types +import zlib + +def gcd(p, q): + """Returns the greatest common divisor of p and q + + + >>> gcd(42, 6) + 6 + """ + if p<q: return gcd(q, p) + if q == 0: return p + return gcd(q, abs(p%q)) + +def bytes2int(bytes): + """Converts a list of bytes or a string to an integer + + >>> (128*256 + 64)*256 + + 15 + 8405007 + >>> l = [128, 64, 15] + >>> bytes2int(l) + 8405007 + """ + + if not (type(bytes) is types.ListType or type(bytes) is types.StringType): + raise TypeError("You must pass a string or a list") + + # Convert byte stream to integer + integer = 0 + for byte in bytes: + integer *= 256 + if type(byte) is types.StringType: byte = ord(byte) + integer += byte + + return integer + +def int2bytes(number): + """Converts a number to a string of bytes + + >>> bytes2int(int2bytes(123456789)) + 123456789 + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + string = "" + + while number > 0: + string = "%s%s" % (chr(number & 0xFF), string) + number /= 256 + + return string + +def fast_exponentiation(a, p, n): + """Calculates r = a^p mod n + """ + result = a % n + remainders = [] + while p != 1: + remainders.append(p & 1) + p = p >> 1 + while remainders: + rem = remainders.pop() + result = ((a ** rem) * result ** 2) % n + return result + +def read_random_int(nbits): + """Reads a random integer of approximately nbits bits rounded up + to whole bytes""" + + nbytes = ceil(nbits/8) + randomdata = os.urandom(nbytes) + return bytes2int(randomdata) + +def ceil(x): + """ceil(x) -> int(math.ceil(x))""" + + return int(math.ceil(x)) + +def randint(minvalue, maxvalue): + """Returns a random integer x with minvalue <= x <= maxvalue""" + + # Safety - get a lot of random data even if the range is fairly + # small + min_nbits = 32 + + # The range of the random numbers we need to generate + range = maxvalue - minvalue + + # Which is this number of bytes + rangebytes = ceil(math.log(range, 2) / 8) + + # Convert to bits, but make sure it's always at least min_nbits*2 + rangebits = max(rangebytes * 8, min_nbits * 2) + + # Take a random number of bits between min_nbits and rangebits + nbits = random.randint(min_nbits, rangebits) + + return (read_random_int(nbits) % range) + minvalue + +def fermat_little_theorem(p): + """Returns 1 if p may be prime, and something else if p definitely + is not prime""" + + a = randint(1, p-1) + return fast_exponentiation(a, p-1, p) + +def jacobi(a, b): + """Calculates the value of the Jacobi symbol (a/b) + """ + + if a % b == 0: + return 0 + result = 1 + while a > 1: + if a & 1: + if ((a-1)*(b-1) >> 2) & 1: + result = -result + b, a = a, b % a + else: + if ((b ** 2 - 1) >> 3) & 1: + result = -result + a = a >> 1 + return result + +def jacobi_witness(x, n): + """Returns False if n is an Euler pseudo-prime with base x, and + True otherwise. + """ + + j = jacobi(x, n) % n + f = fast_exponentiation(x, (n-1)/2, n) + + if j == f: return False + return True + +def randomized_primality_testing(n, k): + """Calculates whether n is composite (which is always correct) or + prime (which is incorrect with error probability 2**-k) + + Returns False if the number if composite, and True if it's + probably prime. + """ + + q = 0.5 # Property of the jacobi_witness function + + # t = int(math.ceil(k / math.log(1/q, 2))) + t = ceil(k / math.log(1/q, 2)) + for i in range(t+1): + x = randint(1, n-1) + if jacobi_witness(x, n): return False + + return True + +def is_prime(number): + """Returns True if the number is prime, and False otherwise. + + >>> is_prime(42) + 0 + >>> is_prime(41) + 1 + """ + + """ + if not fermat_little_theorem(number) == 1: + # Not prime, according to Fermat's little theorem + return False + """ + + if randomized_primality_testing(number, 5): + # Prime, according to Jacobi + return True + + # Not prime + return False + + +def getprime(nbits): + """Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In + other words: nbits is rounded up to whole bytes. + + >>> p = getprime(8) + >>> is_prime(p-1) + 0 + >>> is_prime(p) + 1 + >>> is_prime(p+1) + 0 + """ + + nbytes = int(math.ceil(nbits/8)) + + while True: + integer = read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if is_prime(integer): break + + # Retry if not prime + + return integer + +def are_relatively_prime(a, b): + """Returns True if a and b are relatively prime, and False if they + are not. + + >>> are_relatively_prime(2, 3) + 1 + >>> are_relatively_prime(2, 4) + 0 + """ + + d = gcd(a, b) + return (d == 1) + +def find_p_q(nbits): + """Returns a tuple of two different primes of nbits bits""" + + p = getprime(nbits) + while True: + q = getprime(nbits) + if not q == p: break + + return (p, q) + +def extended_euclid_gcd(a, b): + """Returns a tuple (d, i, j) such that d = gcd(a, b) = ia + jb + """ + + if b == 0: + return (a, 1, 0) + + q = abs(a % b) + r = long(a / b) + (d, k, l) = extended_euclid_gcd(b, q) + + return (d, l, k - l*r) + +# Main function: calculate encryption and decryption keys +def calculate_keys(p, q, nbits): + """Calculates an encryption and a decryption key for p and q, and + returns them as a tuple (e, d)""" + + n = p * q + phi_n = (p-1) * (q-1) + + while True: + # Make sure e has enough bits so we ensure "wrapping" through + # modulo n + e = getprime(max(8, nbits/2)) + if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break + + (d, i, j) = extended_euclid_gcd(e, phi_n) + + if not d == 1: + raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n)) + + if not (e * i) % phi_n == 1: + raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n)) + + return (e, i) + + +def gen_keys(nbits): + """Generate RSA keys of nbits bits. Returns (p, q, e, d). + + Note: this can take a long time, depending on the key size. + """ + + while True: + (p, q) = find_p_q(nbits) + (e, d) = calculate_keys(p, q, nbits) + + # For some reason, d is sometimes negative. We don't know how + # to fix it (yet), so we keep trying until everything is shiny + if d > 0: break + + return (p, q, e, d) + +def gen_pubpriv_keys(nbits): + """Generates public and private keys, and returns them as (pub, + priv). + + The public key consists of a dict {e: ..., , n: ....). The private + key consists of a dict {d: ...., p: ...., q: ....). + """ + + (p, q, e, d) = gen_keys(nbits) + + return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} ) + +def encrypt_int(message, ekey, n): + """Encrypts a message using encryption key 'ekey', working modulo + n""" + + if type(message) is types.IntType: + return encrypt_int(long(message), ekey, n) + + if not type(message) is types.LongType: + raise TypeError("You must pass a long or an int") + + if message > 0 and \ + math.floor(math.log(message, 2)) > math.floor(math.log(n, 2)): + raise OverflowError("The message is too long") + + return fast_exponentiation(message, ekey, n) + +def decrypt_int(cyphertext, dkey, n): + """Decrypts a cypher text using the decryption key 'dkey', working + modulo n""" + + return encrypt_int(cyphertext, dkey, n) + +def sign_int(message, dkey, n): + """Signs 'message' using key 'dkey', working modulo n""" + + return decrypt_int(message, dkey, n) + +def verify_int(signed, ekey, n): + """verifies 'signed' using key 'ekey', working modulo n""" + + return encrypt_int(signed, ekey, n) + +def picklechops(chops): + """Pickles and base64encodes it's argument chops""" + + value = zlib.compress(dumps(chops)) + encoded = base64.encodestring(value) + return encoded.strip() + +def unpicklechops(string): + """base64decodes and unpickes it's argument string into chops""" + + return loads(zlib.decompress(base64.decodestring(string))) + +def chopstring(message, key, n, funcref): + """Splits 'message' into chops that are at most as long as n, + converts these into integers, and calls funcref(integer, key, n) + for each chop. + + Used by 'encrypt' and 'sign'. + """ + + msglen = len(message) + mbits = msglen * 8 + nbits = int(math.floor(math.log(n, 2))) + nbytes = nbits / 8 + blocks = msglen / nbytes + + if msglen % nbytes > 0: + blocks += 1 + + cypher = [] + + for bindex in range(blocks): + offset = bindex * nbytes + block = message[offset:offset+nbytes] + value = bytes2int(block) + cypher.append(funcref(value, key, n)) + + return picklechops(cypher) + +def gluechops(chops, key, n, funcref): + """Glues chops back together into a string. calls + funcref(integer, key, n) for each chop. + + Used by 'decrypt' and 'verify'. + """ + message = "" + + chops = unpicklechops(chops) + + for cpart in chops: + mpart = funcref(cpart, key, n) + message += int2bytes(mpart) + + return message + +def encrypt(message, key): + """Encrypts a string 'message' with the public key 'key'""" + + return chopstring(message, key['e'], key['n'], encrypt_int) + +def sign(message, key): + """Signs a string 'message' with the private key 'key'""" + + return chopstring(message, key['d'], key['p']*key['q'], decrypt_int) + +def decrypt(cypher, key): + """Decrypts a cypher with the private key 'key'""" + + return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int) + +def verify(cypher, key): + """Verifies a cypher with the public key 'key'""" + + return gluechops(cypher, key['e'], key['n'], encrypt_int) + +# Do doctest if we're not imported +if __name__ == "__main__": + import doctest + doctest.testmod() + +__all__ = ["gen_pubpriv_keys", "encrypt", "decrypt", "sign", "verify"] + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |