Commit 52f7a3de authored by Alvin Natawiguna's avatar Alvin Natawiguna
Browse files

Somewhat 90% done, don't know whether it's runnable or not

parent 33240754
......@@ -6,6 +6,7 @@ import game
class Command(object):
__metaclass__ = abc.ABCMeta
"""
Abstract base class for commands
"""
......@@ -28,6 +29,10 @@ class Command(object):
METHOD_FETCHITEM = "fetchitem"
METHOD_CANCELOFFER = "canceloffer"
RESULT_OK = "ok"
RESULT_FAIL = "fail"
RESULT_ERROR = "error"
def __init__(self):
super().__init__()
......@@ -101,7 +106,10 @@ class CommandFactory(object):
elif method == Command.METHOD_SENDFIND:
return SendFindCommand(self.game, self.__json["token"], self.__json["item"])
elif method == Command.METHOD_FINDOFFER:
return FindOfferCommand(self.__json["item"], self.__json["ip"], self.__json["port"])
if 'ip' in self.__json and 'port' in self.__json:
return FindOfferCommand(self.__json["item"], self.__json["ip"], self.__json["port"])
else:
return RecvFindOfferCommand(self.__json["item"])
elif method == Command.METHOD_SENDACCEPT:
return SendAcceptCommand(self.game, self.__json["token"], self.__json["offer_token"])
elif method == Command.METHOD_ACCEPT:
......@@ -170,11 +178,14 @@ class ServerStatusCommand(Command):
def execute(self):
try:
with pymongo.MongoClient().get_database(game.Game.DB_NAME) as db:
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
collection = db.get_collection(ServerStatusCommand.DB_COLLECTION_SERVER)
for server in enumerate(self.servers):
collection.update_one({'ip': server['ip']}, {'$set': {'port': server['port']} })
collection.update_one({'ip': server['ip']}, {'$set': {'ip': server['ip'],'port': server['port']} })
except Exception:
self.__error = True
......@@ -192,7 +203,8 @@ class SignupCommand(Command):
try:
game.playerSignup(self.username, self.password)
with pymongo.MongoClient().get_database(game.Game.DB_NAME) as db:
with pymongo.MongoClient(game.Game.DB_URI) as client:
db = client.get_database(game.Game.DB_NAME)
db.users.insert({
game.Player.KEY_USERNAME: self.username,
game.Player.KEY_PASSWORD: bcrypt.hashpw(self.password, bcrypt.gensalt()),
......@@ -201,9 +213,7 @@ class SignupCommand(Command):
game.Player.KEY_CURRENTLOCATION: {
'x': 0,
'y': 0
},
game.Player.KEY_OFFERS: []
}
})
except Exception as e:
......@@ -291,12 +301,71 @@ class MixItemCommand(Command):
self.result = None
def execute(self):
player = game.getPlayer(token = self.token)
if player:
pass
else:
try:
player = self.game.getPlayer(token = self.token)
if player:
item1 = player.getItem(self.item1)
item2 = player.getItem(self.item2)
newItem, new = player.mixItems(item1, item2)
# update the inventory in the db
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
request = [
pymongo.UpdateOne({
'username': player.username,
'item.id': item1.id
}, {
'$set': {
'items.$.count': item1.count
}
}),
pymongo.UpdateOne({
'username': player.username,
'item.id': item2.id
}, {
'$set': {
'items.$.count': item2.count
}
}),
]
if new:
request.append(pymongo.UpdateOne({
'username': player.username,
}, {
'$addToSet': {
'items': {
'id': newItem,
'count': 1
}
}
}))
else:
request.append(pymongo.UpdateOne({
'username': player.username,
'item.id': newItem
}, {
'$inc': {
'items.$.count': 1
}
}))
response = db.users.bulk_write(request)
self.result = newItem
else:
self.__error = True
except ValueError as valError:
self.__error = str(valError)
except Exception as e:
self.__error = True
print (e)
def getStatus(self):
if self.result:
......@@ -355,9 +424,25 @@ class MoveCommand(Command):
self.__error = True
else:
try:
player.moveTo(x = x, y = y)
player.moveTo(x = self.x, y = self.y)
# TODO: update the player's position in the db
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
result = db.users.update_one({
'username': player.username
}, {
'$set': {
'currentLocation': {
'x': self.x,
'y': self.y
}
}
})
assert result.modified_count == 1
except Exception as e:
self.__error = str(e)
......@@ -389,7 +474,31 @@ class GetItemFromFieldCommand(Command):
try:
self.item = player.takeItem(location = player.currentLocation)
# TODO: update the player's inventory in the db
with pymongo.MongoClient(game.Game.DB_URI) as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
result = db.users.update_one({
'username': player.username,
'items.id': self.item.id
}, {
'$inc': {
'items.$.count': 1
}
})
# check whether the item does not exist
if result.modified_count == 0:
result = db.users.update_one({
'username': player.username
}, {
'$addToSet': {
'items': {
'id': self.item.id,
'count': 1
}
}
})
except Exception as e:
self.__error = str(e)
......@@ -397,7 +506,7 @@ class GetItemFromFieldCommand(Command):
if not self.__error and self.result:
return {
'status': ok,
'item': self.item
'item': self.item.id
}
else:
if not self.__error:
......@@ -419,10 +528,8 @@ class MakeItemOfferCommand(Command):
try:
player = self.game.getPlayer(self.token)
if player:
player.makeOffer(self.offer_item, self.demand_item)
# TODO: add the offer to the global offer table, and update the player's offer table
if player.checkMakeItemOffer(self.offer_item):
offerId = self.game.makeOffer(self.offer_item, self.demand_item)
else:
self.__error = True
except Exception as e:
......@@ -442,7 +549,7 @@ class TradeboxQueryCommand(Command):
try:
player = self.game.getPlayer(self.token)
if player:
for offer in enumerate(player.getTradebox()):
for idx, offer in enumerate(player.getTradebox()):
self.result.append(offer.asList())
else:
self.__error = True
......@@ -531,6 +638,45 @@ class FindOfferCommand(Command):
super().getStatus()
class RecvFindOfferCommand(Command):
def __init__(self, item):
super().__init__()
self.item = item
self.result = []
def execute(self):
try:
with pymongo.MongoClient(game.Game.DB_URI) as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
cursor = db.offers.find({'offer_item.id' : self.item})
for offer in cursor:
self.result.append([
offer['offer_item']['id'], offer['offer_item']['count'],
offer['demand_item']['id'], offer['demand_item']['count'],
offer['avaliable'], offer['id']
])
except Exception as e:
self.__error = str(e)
print (e)
def getStatus(self):
if self.result and not self.__error:
return {
'status': 'ok',
'offers': self.result
}
else:
if not self.__error:
self.__error = True
super().getStatus()
class SendItemAcceptOfferCommand(Command):
def __init__(self, game, token, offer_token):
......@@ -541,8 +687,70 @@ class SendItemAcceptOfferCommand(Command):
self.offer_token = offer_token
def execute(self):
# TODO: accept the player's offer
pass
try:
player = self.game.getPlayer(self.token)
if player:
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
offer = db.offers.find_one({'id': self.offer_token})
if offer:
if offer['available']:
demandedItem = game.Item(offer['demand']['id'], offer['demand']['count'])
try:
if player.checkAcceptOfferedItem(demandedItem):
# accept the local offer
result = db.offers.update_one({
'id': self.offer_token
}, {
'$set': {
'available': False
}
})
assert result.modified_count == 1
player.acceptOffer(demandedItem)
else:
self.__error = "Insufficient items"
except Exception as e:
self.__error = str(e)
print (e)
else:
self.__error = "Offer not available"
# end of local offer
else:
# check the cached offers
offer = db.cachedOffers.find_one({'id': self.offer_token})
if offer:
demandedItem = game.Item(offer['demand']['id'], offer['demand']['count'])
if player.checkAcceptOfferedItem(demandedItem):
# accept the cached offer
command = AcceptItemOfferCommand(offer['offer_token'], offer['ip'], offer['port'])
command.execute()
result = command.getStatus()
if result['status'] == "ok":
# create a duplicate in the offer
self.game.duplicateAcceptedOffer(demandedItem,
game.Item(offer['offer']['id'], offer['offer']['count']),
offer['id'])
player.acceptOffer(demandedItem)
else:
if result['status'] == Command.RESULT_FAIL:
self.__error = result['description']
else:
self.__error = True
else: # end of checking cached offers
self.__error = "No such offer exists"
else: # player checking
self.__error = True
except Exception as e:
self.__error = str(e)
print (e)
# NOTE: this is something that is sent by the server, not received by the server
class AcceptItemOfferCommand(Command):
......@@ -558,9 +766,70 @@ class AcceptItemOfferCommand(Command):
self.result = None
def execute(self):
# TODO: accept the player's offer from other server
# Question: do we need to duplicate?
pass
sock = socket.socket()
try:
sock.settimeout(3)
sock.connect((self.ip, self.port))
sock.sendAll(json.dumps({
'method': Command.METHOD_ACCEPT,
'offer_token': self.offer_token
}))
"""
This assumes that the server is going to send small-sized data.
Maybe in the future this needs to be revised, to handle arbitary-sized data.
"""
data = sock.recv(4096, 'utf-8')
self.result = json.loads(data)
except Exception as e:
self.__error = str(e)
print (e)
finally:
sock.close()
def getStatus(self):
if not self.__error and self.result:
return self.result
else:
if not self.__error:
self.__error = True
super().getStatus()
class RecvAcceptItemOfferCommand(Command):
def __init__(self, offer_token):
super().__init__()
self.offer_token = offer_token
def execute(self):
try:
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
item = db.offers.find_one({'id' : self.offer_token})
if item:
if item['avaliable']:
db.offers.update_one({
'id' : self.offer_token
}, {
'$set': {
'available': False
}
})
else:
self.__error = "Offer is not avaliable"
else:
self.__error = "No such offer exists"
except Exception as e:
self.__error = True
print (e)
class FetchOfferedItemCommand(Command):
def __init__(self, game, token, offer_token):
......@@ -571,8 +840,50 @@ class FetchOfferedItemCommand(Command):
self.offer_token = offer_token
def execute(self):
# TODO: fetch the player's offer
pass
try:
player = self.game.getPlayer(self.token)
if player:
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
offer = db.offers.find_one({'id': self.offer_token})
if offer: # offer is in DB
if not item['available']:
if player.fetchOffer(game.Item(offer['offer']['id'], offer['offer']['count'])):
result = db.users.update_one({
'username': player.username,
'items.id': offer['offer']['id']
}, {
'$inc': {
'items.$.count': offer['offer']['count']
}
})
assert result.modified_count == 1
else:
result = db.users.update_one({
'username': player.username
}, {
'$addToSet': {
'items': {
'id': offer['offer']['id'],
'count': offer['offer']['count']
}
}
})
assert result.modified_count == 1
db.offers.delete_one({
'_id': offer['_id']
})
else:
self.__error = "Item is still being offered!"
else:
self.__error = True
except Exception as e:
self.__error = True
print(e)
class CancelItemOfferCommand(Command):
def __init__(self, game, token, offer_token):
......@@ -583,5 +894,49 @@ class CancelItemOfferCommand(Command):
self.offer_token = offer_token
def execute(self):
# TODO: cancel the player's offer
pass
\ No newline at end of file
try:
player = self.game.getPlayer(self.token)
if player:
with pymongo.MongoClient() as client:
db = client.get_database(game.Game.DB_NAME)
assert db.authenticate(game.Game.DB_USERNAME, game.Game.DB_PASSWORD)
offer = db.offers.find_one({'id': self.offer_token})
if offer: # offer is in DB
if item['available']:
if player.cancelOffer(game.Item(offer['demand']['id'], offer['demand']['count'])):
result = db.users.update_one({
'username': player.username,
'items.id': offer['demand']['id']
}, {
'$inc': {
'items.$.count': offer['demand']['count']
}
})
assert result.modified_count == 1
else:
result = db.users.update_one({
'username': player.username
}, {
'$addToSet': {
'items': {
'id': offer['demand']['id'],
'count': offer['demand']['count']
}
}
})
assert result.modified_count == 1
db.offers.delete_one({
'_id': offer['_id']
})
else:
self.__error = "Item has already been taken!"
else: # offer is not in DB
self.__error = "Offer does not exist!"
else:
self.__error = True
except Exception as e:
self.__error = True
print(e)
\ No newline at end of file
......@@ -33,6 +33,10 @@ class Game(object):
DB_NAME = 'if3230-sister'
DB_USERNAME = "sister"
DB_PASSWORD = "sisterpoi"
DB_HOST = "127.0.0.1"
DB_COLLECTION_USERS = 'users'
__instance = None
......@@ -43,6 +47,7 @@ class Game(object):
# the attributes
self.players = []
self.map = None
self.offers = []
with open(mapFile) as map_file:
self.__loadMap(json.load(mapFile))
......@@ -63,15 +68,19 @@ class Game(object):
def __loadPlayers(self):
self.players = []
with pymongo.MongoClient().get_database(Game.DB_NAME) as db:
with pymongo.MongoClient() as client:
db = client.get_database(Game.DB_NAME)
assert db.authenticate(Game.DB_USERNAME, Game.DB_PASSWORD)
# load the players from the db
cursor = db.get_collection(Game.DB_COLLECTION_USERS)
for player in cursor.find():
for player in cursor.find({}):
location = Location(player['location']['x'], player['location']['y'])
players.append(Player(self, player['username'], player['password'], location))
players.append(Player(self, player['username'], player['password'].encode(), location))
def __loadOffers(self):
pass
"""
Do a player login
......@@ -83,7 +92,7 @@ class Game(object):
Returns the authentication token
"""
def playerLogin(self, username, password):
for player in enumerate(self.players):
for idx, player in enumerate(self.players):
if player.username == username:
break
......@@ -111,7 +120,7 @@ class Game(object):
password -- the Player's password in plaintext
"""
def playerSignup(self, username, password):
for player in enumerate(self.players):
for idx, player in enumerate(self.players):
if player.username == username:
raise ValueError('Username already exists')
......@@ -132,7 +141,7 @@ class Game(object):
"""
def getPlayer(self, token = None, username = None):
if token or username:
for player in enumerate(self.players):
for idx, player in enumerate(self.players):
assert isinstance(player.username, str)
if player.username == username or (token and player.token == token):
......@@ -142,4 +151,79 @@ class Game(object):
return None
else:
raise TypeError('No search argument (token or username) defined')
\ No newline at end of file
raise TypeError('No search argument (token or username) defined')
def makeOffer(self, username, demandedItem, offeredItem):
with pymongo.MongoClient() as client:
db = client.get_database(Game.DB_NAME)
assert db.authenticate(Game.DB_USERNAME, Game.DB_PASSWORD)
# deduct the item from the db
result = db.users.update_one({
'username': username,
'items.id': offeredItem.id
}, {
'$inc': {
'items.$.count': -offeredItem.count
}
})
assert result.modified_count == 1
newOffer = game.ItemOffer(username, demandedItem, offeredItem)
self.offers.append(newOffer)