historical/toontown-classic.git/toontown/estate/DistributedFurnitureManagerAI.py
2024-01-16 11:20:27 -06:00

611 lines
24 KiB
Python

import datetime
import json
import os
import time
from direct.directnotify import DirectNotifyGlobal
from direct.distributed.DistributedObjectAI import DistributedObjectAI
from toontown.catalog import CatalogFurnitureItem
from toontown.catalog import CatalogInvalidItem
from toontown.catalog import CatalogItem
from toontown.catalog import CatalogSurfaceItem
from toontown.catalog import CatalogWindowItem
from toontown.catalog.CatalogItemList import CatalogItemList
from toontown.estate.DistributedBankAI import DistributedBankAI
from toontown.estate.DistributedClosetAI import DistributedClosetAI
from toontown.estate.DistributedFurnitureItemAI import DistributedFurnitureItemAI
from toontown.estate.DistributedPhoneAI import DistributedPhoneAI
from toontown.estate.DistributedTrunkAI import DistributedTrunkAI
from toontown.toonbase import ToontownGlobals
MINUTE = 60
HOUR = 60 * MINUTE
DAY = 24 * HOUR
def getDayId():
return int(time.time() // DAY)
class DistributedFurnitureManagerAI(DistributedObjectAI):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFurnitureManagerAI')
serverDataFolder = simbase.config.GetString('server-data-folder', '')
def __init__(self, air, house, interior):
DistributedObjectAI.__init__(self, air)
self.house = house
self.interior = interior
self.ownerId = 0
self.ownerName = ''
self.interiorId = 0
self.atticItems = CatalogItemList(store=CatalogItem.Customization)
self.atticWallpaper = CatalogItemList(store=CatalogItem.Customization)
self.atticWindows = CatalogItemList(store=CatalogItem.Customization)
self.deletedItems = CatalogItemList(store=CatalogItem.Customization)
self.wallpaper = CatalogItemList(store=CatalogItem.Customization)
self.windows = CatalogItemList(store=CatalogItem.Customization | CatalogItem.WindowPlacement)
self.items = []
self.director = 0
self.deletedItemsFilename = ''
self.day2deletedItems = {}
def announceGenerate(self):
DistributedObjectAI.announceGenerate(self)
self.deletedItemsFilename = self.getDeletedItemsFilename()
self.day2deletedItems = self.loadDeletedItems()
taskMgr.add(self.__deletedItemsTask, self.uniqueName('deleted-items-task'))
def delete(self):
for item in self.items[:]:
item.requestDelete()
self.items.remove(item)
taskMgr.remove(self.uniqueName('deleted-items-task'))
DistributedObjectAI.delete(self)
def setOwnerId(self, ownerId):
self.ownerId = ownerId
def d_setOwnerId(self, ownerId):
self.sendUpdate('setOwnerId', [ownerId])
def b_setOwnerId(self, ownerId):
self.setOwnerId(ownerId)
self.d_setOwnerId(ownerId)
def getOwnerId(self):
return self.ownerId
def setOwnerName(self, ownerName):
self.ownerName = ownerName
def d_setOwnerName(self, ownerName):
self.sendUpdate('setOwnerName', [ownerName])
def b_setOwnerName(self, ownerName):
self.setOwnerName(ownerName)
self.d_setOwnerName(ownerName)
def getOwnerName(self):
return self.ownerName
def setInteriorId(self, interiorId):
self.interiorId = interiorId
def d_setInteriorId(self, interiorId):
self.sendUpdate('setInteriorId', [interiorId])
def b_setInteriorId(self, interiorId):
self.setInteriorId(interiorId)
self.d_setInteriorId(interiorId)
def getInteriorId(self):
return self.interiorId
def setAtticItems(self, atticItems):
self.atticItems = CatalogItemList(atticItems, store=CatalogItem.Customization)
def d_setAtticItems(self, atticItems):
self.sendUpdate('setAtticItems', [atticItems])
def b_setAtticItems(self, atticItems):
self.setAtticItems(atticItems)
self.d_setAtticItems(atticItems)
def getAtticItems(self):
return self.atticItems.getBlob()
def setAtticWallpaper(self, atticWallpaper):
self.atticWallpaper = CatalogItemList(atticWallpaper, store=CatalogItem.Customization)
def d_setAtticWallpaper(self, atticWallpaper):
self.sendUpdate('setAtticWallpaper', [atticWallpaper])
def b_setAtticWallpaper(self, atticWallpaper):
self.setAtticWallpaper(atticWallpaper)
self.d_setAtticWallpaper(atticWallpaper)
def getAtticWallpaper(self):
return self.atticWallpaper.getBlob()
def setAtticWindows(self, atticWindows):
self.atticWindows = CatalogItemList(atticWindows, store=CatalogItem.Customization)
def d_setAtticWindows(self, atticWindows):
self.sendUpdate('setAtticWindows', [atticWindows])
def b_setAtticWindows(self, atticWindows):
self.setAtticWindows(atticWindows)
self.d_setAtticWindows(atticWindows)
def getAtticWindows(self):
return self.atticWindows.getBlob()
def setDeletedItems(self, deletedItems):
self.deletedItems = CatalogItemList(deletedItems, store=CatalogItem.Customization)
def d_setDeletedItems(self, deletedItems):
self.sendUpdate('setDeletedItems', [deletedItems])
def b_setDeletedItems(self, deletedItems):
self.setDeletedItems(deletedItems)
self.d_setDeletedItems(deletedItems)
def getDeletedItems(self):
return self.deletedItems.getBlob()
def suggestDirector(self, avId):
if avId and avId != self.ownerId:
self.air.writeServerEvent('suspicious', avId, 'av tried to manage furniture in someone else\'s house')
return
av = self.air.doId2do.get(avId)
director = self.air.doId2do.get(self.director)
if director:
director.b_setGhostMode(0)
else:
av.b_setGhostMode(1)
self.b_setDirector(avId)
self.saveFurniture()
def setDirector(self, director):
self.director = director
def d_setDirector(self, director):
self.sendUpdate('setDirector', [director])
def b_setDirector(self, director):
self.setDirector(director)
self.d_setDirector(director)
def getDirector(self):
return self.director
def moveItemToAtticMessage(self, itemId, context):
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'moveItemToAttic', context)
if not owner:
return
item = self.air.doId2do.get(itemId)
if not item or item not in self.items:
self.sendUpdateToAvatarId(avId, 'moveItemToAtticResponse', [ToontownGlobals.FM_InvalidItem, context])
self.atticItems.append(item.catalogItem)
self.b_setAtticItems(self.getAtticItems())
item.requestDelete()
self.items.remove(item)
self.sendUpdateToAvatarId(avId, 'moveItemToAtticResponse', [ToontownGlobals.FM_MovedItem, context])
def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'moveItemFromAttic', context)
if not owner:
return
try:
item = self.atticItems[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to move nonexistent attic item')
self.sendUpdateToAvatarId(avId, 'moveItemFromAtticResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
del self.atticItems[index]
self.b_setAtticItems(self.getAtticItems())
item.posHpr = (x, y, z, h, p, r)
obj = self.generateItem(item)
self.sendUpdateToAvatarId(avId, 'moveItemFromAtticResponse', [ToontownGlobals.FM_MovedItem, obj.doId, context])
def deleteItemFromAtticMessage(self, blob, index, context):
# blob is USELESS
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'deleteItemFromAttic', context)
if not owner:
return
try:
item = self.atticItems[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to delete nonexistent attic item')
self.sendUpdateToAvatarId(avId, 'deleteItemFromAtticResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
if not item.isDeletable():
self.sendUpdateToAvatarId(avId, 'deleteItemFromAtticResponse',
[ToontownGlobals.FM_NondeletableItem, context])
return
del self.atticItems[index]
self.b_setAtticItems(self.getAtticItems())
self._deleteItem(item)
self.sendUpdateToAvatarId(avId, 'deleteItemFromAtticResponse', [ToontownGlobals.FM_DeletedItem, context])
def deleteItemFromRoomMessage(self, blob, itemId, context):
# blob has no use here either
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'deleteItemFromRoom', context)
if not owner:
return
item = self.air.doId2do.get(itemId)
if not item or item not in self.items:
self.sendUpdateToAvatarId(avId, 'deleteItemFromRoomResponse', [ToontownGlobals.FM_InvalidItem, context])
return
if not item.catalogItem.isDeletable():
self.sendUpdateToAvatarId(avId, 'deleteItemFromRoomResponse',
[ToontownGlobals.FM_NondeletableItem, context])
return
self.items.remove(item)
self._deleteItem(item.catalogItem)
item.requestDelete()
self.sendUpdateToAvatarId(avId, 'deleteItemFromRoomResponse', [ToontownGlobals.FM_DeletedItem, context])
def moveWallpaperFromAtticMessage(self, index, room, context):
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'moveWallpaperFromAttic', context)
if not owner:
return
try:
item = self.atticWallpaper[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to move nonexistent wallpaper')
self.sendUpdateToAvatarId(avId, 'moveWallpaperFromAtticResponse',
[ToontownGlobals.FM_InvalidIndex, context])
return
slot = room * CatalogSurfaceItem.NUM_ST_TYPES + item.getSurfaceType()
self.atticWallpaper[index] = self.wallpaper[slot]
self.b_setAtticWallpaper(self.getAtticWallpaper())
self.wallpaper[slot] = item
self.house.interior.b_setWallpaper(self.wallpaper.getBlob())
self.sendUpdateToAvatarId(avId, 'moveWallpaperFromAtticResponse', [ToontownGlobals.FM_SwappedItem, context])
def deleteWallpaperFromAtticMessage(self, blob, index, context):
# you didn't expect blob to be used, right?
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'deleteWallpaperFromAttic', context)
if not owner:
return
try:
item = self.atticWallpaper[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to delete nonexistent wallpaper')
self.sendUpdateToAvatarId(avId, 'deleteWallpaperFromAtticResponse',
[ToontownGlobals.FM_InvalidIndex, context])
return
del self.atticWallpaper[index]
self.b_setAtticWallpaper(self.getAtticWallpaper())
self._deleteItem(item)
self.sendUpdateToAvatarId(avId, 'deleteWallpaperFromAtticResponse', [ToontownGlobals.FM_DeletedItem, context])
def moveWindowToAtticMessage(self, slot, context):
# meme
pass
def moveWindowFromAtticMessage(self, index, slot, context):
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'moveWindowFromAttic', context)
if not owner:
return
try:
item = self.atticWindows[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to move nonexistent window')
self.sendUpdateToAvatarId(avId, 'moveWindowFromAtticResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
if slot > 5:
self.air.writeServerEvent('suspicious', avId, 'av tried to put window in invalid slot!')
return
item.placement = slot
oldWindow = None
windowIndex = None
for i, window in enumerate(self.windows):
if window.placement == slot:
oldWindow = window
windowIndex = i
if not oldWindow:
self.sendUpdateToAvatarId(avId, 'moveWindowFromAtticResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
self.atticWindows[index] = oldWindow
self.d_setAtticWindows(self.getAtticWindows())
self.windows[windowIndex] = item
self.house.interior.b_setWindows(self.windows.getBlob())
self.sendUpdateToAvatarId(avId, 'moveWindowFromAtticResponse', [ToontownGlobals.FM_SwappedItem, context])
def moveWindowMessage(self, fromSlot, toSlot, context):
# yes
pass
def deleteWindowFromAtticMessage(self, blob, index, context):
# what is blob
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'deleteWindowFromAttic', context)
if not owner:
return
try:
item = self.atticWindows[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to delete nonexistent window')
self.sendUpdateToAvatarId(avId, 'deleteWindowFromAtticResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
del self.atticWindows[index]
self.d_setAtticWindows(self.getAtticWindows())
self._deleteItem(item)
self.sendUpdateToAvatarId(avId, 'deleteWindowFromAtticResponse', [ToontownGlobals.FM_DeletedItem, context])
def recoverDeletedItemMessage(self, blob, index, context):
# blob.
avId = self.air.getAvatarIdFromSender()
owner = self._verifyOwner(avId, 'recoverDeletedItem', context)
if not owner:
return
numItems = len(self.atticItems) + len(self.atticWallpaper) + len(self.atticWindows) + len(self.items)
if numItems + 1 > ToontownGlobals.MaxHouseItems:
self.sendUpdateToAvatarId(avId, 'recoverDeletedItemResponse', [ToontownGlobals.FM_HouseFull, context])
return
try:
item = self.deletedItems[index]
except KeyError:
self.air.writeServerEvent('suspicious', avId, 'av tried to recover nonexistent item')
self.sendUpdateToAvatarId(avId, 'recoverDeletedItemResponse', [ToontownGlobals.FM_InvalidIndex, context])
return
type2attic = {
CatalogSurfaceItem.CatalogSurfaceItem: (
self.atticWallpaper, self.b_setAtticWallpaper, self.getAtticWallpaper),
CatalogFurnitureItem.CatalogFurnitureItem: (self.atticItems, self.b_setAtticItems, self.getAtticItems),
CatalogWindowItem.CatalogWindowItem: (self.atticWindows, self.b_setAtticWindows, self.getAtticWindows)
}
for itemType in type2attic.keys():
if isinstance(item, itemType):
attic, setter, getter = type2attic[itemType]
attic.append(item)
setter(getter())
del self.deletedItems[index]
self.b_setDeletedItems(self.getDeletedItems())
self.removeDeletedItemBlob(item.getBlob().encode('base64'))
self.sendUpdateToAvatarId(avId, 'recoverDeletedItemResponse',
[ToontownGlobals.FM_RecoveredItem, context])
def loadFurniture(self):
for item in CatalogItemList(self.house.getInteriorItems(),
store=CatalogItem.Customization | CatalogItem.Location):
self.generateItem(item)
self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(), store=CatalogItem.Customization)
self.house.b_setInteriorWallpaper(self.house.getInteriorWallpaper())
self.interior.b_setWallpaper(self.house.getInteriorWallpaper())
self.windows = CatalogItemList(self.house.getInteriorWindows(),
store=CatalogItem.Customization | CatalogItem.WindowPlacement)
self.house.b_setInteriorWindows(self.house.getInteriorWindows())
self.interior.b_setWindows(self.house.getInteriorWindows())
self.b_setAtticItems(self.house.getAtticItems())
self.b_setAtticWallpaper(self.house.getAtticWallpaper())
self.b_setAtticWindows(self.house.getAtticWindows())
self.b_setDeletedItems(self.house.getDeletedItems())
def generateItem(self, item):
itemId = item.furnitureType
if itemId == 1399:
furnitureClass = DistributedPhoneAI
elif item.getFlags() & CatalogFurnitureItem.FLCloset:
furnitureClass = DistributedClosetAI
elif item.getFlags() & CatalogFurnitureItem.FLBank:
furnitureClass = DistributedBankAI
elif item.getFlags() & CatalogFurnitureItem.FLTrunk:
furnitureClass = DistributedTrunkAI
else:
furnitureClass = DistributedFurnitureItemAI
obj = furnitureClass(self.air, self.house, self, item)
obj.generateWithRequired(self.interior.zoneId)
self.items.append(obj)
return obj
def saveFurniture(self):
self.house.b_setInteriorItems(self.getNewItems())
self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
self.house.b_setInteriorWindows(self.windows.getBlob())
self.interior.b_setWindows(self.windows.getBlob())
self.interior.b_setWallpaper(self.wallpaper.getBlob())
self.house.b_setAtticItems(self.getAtticItems())
self.b_setAtticItems(self.getAtticItems())
self.house.b_setAtticWallpaper(self.getAtticWallpaper())
self.b_setAtticWallpaper(self.getAtticWallpaper())
self.house.b_setAtticWindows(self.getAtticWindows())
self.b_setAtticWindows(self.getAtticWindows())
self.house.b_setDeletedItems(self.getDeletedItems())
self.b_setDeletedItems(self.getDeletedItems())
def getNewItems(self):
items = CatalogItemList(store=CatalogItem.Customization | CatalogItem.Location)
for item in self.items:
items.append(item.catalogItem)
return items.getBlob()
def _verifyOwner(self, avId, message, context):
if self.house.avatarId != avId:
self.air.writeServerEvent('suspicious', avId,
'av tried to send perform furniture management operation %s while not owner!' % message)
self.sendUpdateToAvatarId(avId, message + 'Response', [ToontownGlobals.FM_NotOwner, context])
return False
elif self.director != avId:
self.sendUpdateToAvatarId(avId, message + 'Response', [ToontownGlobals.FM_NotDirector, context])
return False
return True
def _deleteItem(self, item):
self.deletedItems.append(item)
if len(self.deletedItems) > ToontownGlobals.ExtraDeletedItems:
del self.deletedItems[0]
self.b_setDeletedItems(self.getDeletedItems())
self.addDeletedItemBlob(item.getBlob().encode('base64'))
def getNumItems(self):
numItems = len(self.house.interiorItems) + len(self.house.atticItems) + len(self.house.atticWallpaper) + len(
self.house.atticWindows) + len(self.house.interiorWallpaper) + len(self.house.interiorWindows)
return numItems
def getDeletedItemsFilename(self):
return '%s%s%s%s.json' % (self.serverDataFolder, 'houses/', 'deletedItems_', self.house.getDoId())
def loadDeletedItems(self):
try:
deletedItemsFile = open(self.deletedItemsFilename, 'r')
deletedItemsData = json.load(deletedItemsFile)
return deletedItemsData
except:
return {}
def updateDeletedItemsFile(self):
try:
if not os.path.exists(os.path.dirname(self.deletedItemsFilename)):
os.makedirs(os.path.dirname(self.deletedItemsFilename))
deletedItemsFile = open(self.deletedItemsFilename, 'w')
deletedItemsFile.seek(0)
json.dump(self.day2deletedItems, deletedItemsFile)
deletedItemsFile.close()
except:
pass
def addDeletedItemBlob(self, deletedItemBlob, dayId=None):
if dayId is None:
dayId = getDayId()
dayId = str(dayId)
if dayId not in self.day2deletedItems.keys():
self.day2deletedItems[dayId] = []
self.day2deletedItems[dayId].append(deletedItemBlob)
self.updateDeletedItemsFile()
def removeDeletedItemBlob(self, deletedItemBlob, dayId=None):
if not self.day2deletedItems.keys():
return
if dayId is None:
dayId = getDayId()
dayId = str(dayId)
if dayId not in self.day2deletedItems.keys():
dayId = min(self.day2deletedItems.keys())
elif not self.day2deletedItems[dayId]:
del self.day2deletedItems[dayId]
dayId = min(self.day2deletedItems.keys())
dayId = str(dayId)
if dayId in self.day2deletedItems.keys() and deletedItemBlob in self.day2deletedItems[dayId]:
self.day2deletedItems[dayId].remove(deletedItemBlob)
if not self.day2deletedItems[dayId]:
del self.day2deletedItems[dayId]
self.updateDeletedItemsFile()
def __deletedItemsTask(self, task):
changesMade = False
dayId = getDayId()
for deletedItemDay, deletedItemBlobs in self.day2deletedItems.items():
if not deletedItemBlobs or type(deletedItemBlobs) != list:
del self.day2deletedItems[deletedItemDay]
changesMade = True
continue
try:
deletedItemDayId = int(deletedItemDay)
except:
del self.day2deletedItems[deletedItemDay]
changesMade = True
continue
if deletedItemDayId + int(ToontownGlobals.DeletedItemLifetime / 60 / 24) <= dayId:
for deletedItemBlob in deletedItemBlobs[:]:
try:
deletedItem = CatalogItem.getItem(deletedItemBlob.decode('base64'))
except:
self.day2deletedItems[deletedItemDay].remove(deletedItemBlob)
if not self.day2deletedItems[deletedItemDay]:
del self.day2deletedItems[deletedItemDay]
changesMade = True
continue
if isinstance(deletedItem, CatalogInvalidItem.CatalogInvalidItem):
self.day2deletedItems[deletedItemDay].remove(deletedItemBlob)
if not self.day2deletedItems[deletedItemDay]:
del self.day2deletedItems[deletedItemDay]
changesMade = True
continue
if deletedItem not in self.deletedItems:
self.day2deletedItems[deletedItemDay].remove(deletedItemBlob)
if not self.day2deletedItems[deletedItemDay]:
del self.day2deletedItems[deletedItemDay]
changesMade = True
continue
index = self.deletedItems.index(deletedItem)
del self.deletedItems[index]
self.b_setDeletedItems(self.getDeletedItems())
self.day2deletedItems[deletedItemDay].remove(deletedItemBlob)
if not self.day2deletedItems[deletedItemDay]:
del self.day2deletedItems[deletedItemDay]
changesMade = True
if changesMade:
self.updateDeletedItemsFile()
# We want this task to run again at midnight. We'll calculate the seconds until midnight, then
# delay the task from running again until then.
tomorrow = self.air.toontownTimeManager.getCurServerDateTime().now(
tz=self.air.toontownTimeManager.serverTimeZone) + datetime.timedelta(1)
midnight = datetime.datetime(year=tomorrow.year, month=tomorrow.month, day=tomorrow.day, hour=0, minute=0,
second=0, tzinfo=self.air.toontownTimeManager.serverTimeZone)
secondsUntilMidnight = (midnight - self.air.toontownTimeManager.getCurServerDateTime().now(
tz=self.air.toontownTimeManager.serverTimeZone)).seconds
task.delayTime = secondsUntilMidnight
return task.again