mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-23 11:42:39 -06:00
Merge branches
This commit is contained in:
commit
31bb2cdb75
1680 changed files with 427855 additions and 13 deletions
26
.gitignore
vendored
Executable file
26
.gitignore
vendored
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
# Python artifacts
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
|
||||||
|
# Batch
|
||||||
|
*.bat
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Mac directory info
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Git
|
||||||
|
*.rej
|
||||||
|
|
||||||
|
# Local config
|
||||||
|
dependencies/config/local.prc
|
||||||
|
|
||||||
|
# PyMongo
|
||||||
|
bson
|
||||||
|
gridfs
|
||||||
|
pymongo
|
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[submodule "build/nirai/src"]
|
||||||
|
path = build/nirai/src
|
||||||
|
url = https://github.com/nirai-compiler/src
|
||||||
|
[submodule "build/nirai/panda3d"]
|
||||||
|
path = build/nirai/panda3d
|
||||||
|
url = https://github.com/nirai-compiler/panda3d
|
||||||
|
[submodule "build/nirai/python"]
|
||||||
|
path = build/nirai/python
|
||||||
|
url = https://github.com/nirai-compiler/python
|
13
README.md
13
README.md
|
@ -1,13 +0,0 @@
|
||||||
# Toontown Stride
|
|
||||||
September build of Toontown Stride - open sourced and free to use.
|
|
||||||
|
|
||||||
## Tired of waiting for keys?
|
|
||||||
No need to worry! The official Toontown Stride team welcomes you to **build off any files listed inside the repository after you clone.**
|
|
||||||
|
|
||||||
## Why do this?
|
|
||||||
Since Toontown Stride was originally meant to be a community-based server, we decided to make it *truly* community based on behalf of the original contributers to the source.
|
|
||||||
|
|
||||||
##Is this version safe?
|
|
||||||
Feel free to scan yourself and give us feedback on your findings!
|
|
||||||
|
|
||||||
#Credit goes to Mr. Batty and Alexalexer for this build of the source.
|
|
3
build/.gitignore
vendored
Normal file
3
build/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
libpandadna/
|
||||||
|
built/
|
||||||
|
*.pdb
|
72
build/data/NiraiStart.py
Normal file
72
build/data/NiraiStart.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from panda3d.core import *
|
||||||
|
import __builtin__, os, sys
|
||||||
|
import aes
|
||||||
|
|
||||||
|
import niraidata
|
||||||
|
|
||||||
|
# Config
|
||||||
|
prc = niraidata.CONFIG
|
||||||
|
iv, key, prc = prc[:16], prc[16:32], prc[32:]
|
||||||
|
prc = aes.decrypt(prc, key, iv)
|
||||||
|
|
||||||
|
for line in prc.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
loadPrcFileData('nirai config', line)
|
||||||
|
|
||||||
|
del prc
|
||||||
|
del iv
|
||||||
|
del key
|
||||||
|
|
||||||
|
# DC
|
||||||
|
__builtin__.dcStream = StringStream()
|
||||||
|
|
||||||
|
dc = niraidata.DC
|
||||||
|
iv, key, dc = dc[:16], dc[16:32], dc[32:]
|
||||||
|
dc = aes.decrypt(dc, key, iv)
|
||||||
|
|
||||||
|
dcStream.setData(dc)
|
||||||
|
del dc
|
||||||
|
del iv
|
||||||
|
del key
|
||||||
|
|
||||||
|
# Resources
|
||||||
|
# TO DO: Sign and verify the phases to prevent editing.
|
||||||
|
|
||||||
|
vfs = VirtualFileSystem.getGlobalPtr()
|
||||||
|
mfs = [3, 3.5, 4, 5, 5.5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||||
|
abort = False
|
||||||
|
|
||||||
|
for mf in mfs:
|
||||||
|
filename = 'resources/default/phase_%s.mf' % mf
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
print 'Phase %s not found' % filename
|
||||||
|
abort = True
|
||||||
|
break
|
||||||
|
|
||||||
|
mf = Multifile()
|
||||||
|
mf.openRead(filename)
|
||||||
|
|
||||||
|
if not vfs.mount(mf, '/', 0):
|
||||||
|
print 'Unable to mount %s' % filename
|
||||||
|
abort = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Packs
|
||||||
|
pack = os.environ.get('TT_STRIDE_CONTENT_PACK')
|
||||||
|
import glob
|
||||||
|
if pack and pack != 'default':
|
||||||
|
print 'Loading content pack', pack
|
||||||
|
for file in glob.glob('resources/%s/*.mf' % pack):
|
||||||
|
mf = Multifile()
|
||||||
|
mf.openReadWrite(Filename(file))
|
||||||
|
names = mf.getSubfileNames()
|
||||||
|
for name in names:
|
||||||
|
ext = os.path.splitext(name)[1]
|
||||||
|
if ext not in ['.jpg', '.jpeg', '.ogg', '.rgb']:
|
||||||
|
mf.removeSubfile(name)
|
||||||
|
vfs.mount(mf, Filename('/'), 0)
|
||||||
|
|
||||||
|
if not abort:
|
||||||
|
# Run
|
||||||
|
import toontown.toonbase.ToontownStart
|
3
build/linux/aes-to-so.sh
Executable file
3
build/linux/aes-to-so.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/include -I/usr/include/python2.7 -lstdc++ -lssl -lcrypto -c ../nirai/src/aes.cxx -c -o ../nirai/src/aes.o
|
||||||
|
|
||||||
|
gcc -shared ../nirai/src/aes.o -L/usr/local/lib -lstdc++ -lssl -lcrypto -o ../nirai/src/aes.so
|
2
build/linux/ubuntu-build-panda3d.sh
Executable file
2
build/linux/ubuntu-build-panda3d.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
cd ../nirai/panda3d
|
||||||
|
python2.7 makepanda/makepanda.py --everything --no-contrib --no-fmodex --no-physx --no-bullet --no-pview --no-pandatool --no-swscale --no-swresample --no-speedtree --no-vrpn --no-artoolkit --no-opencv --no-directcam --no-vision --no-rocket --no-awesomium --no-deploytools --no-skel --no-ffmpeg --no-eigen --static
|
141
build/make.py
Normal file
141
build/make.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
from panda3d.core import *
|
||||||
|
|
||||||
|
import argparse, struct
|
||||||
|
import sys, glob
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append('nirai/src')
|
||||||
|
|
||||||
|
from niraitools import *
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--compile-cxx', '-c', action='store_true',
|
||||||
|
help='Compile the CXX codes and generate stride.exe into built.')
|
||||||
|
parser.add_argument('--make-nri', '-n', action='store_true',
|
||||||
|
help='Generate stride NRI.')
|
||||||
|
parser.add_argument('--make-mfs', '-m', action='store_true',
|
||||||
|
help='Make multifiles')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.exists('built'):
|
||||||
|
os.mkdir('built')
|
||||||
|
|
||||||
|
def niraicall_obfuscate(code):
|
||||||
|
# We'll obfuscate if len(code) % 4 == 0
|
||||||
|
# This way we make sure both obfuscated and non-obfuscated code work.
|
||||||
|
if len(code) % 4:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Reverse
|
||||||
|
code = code[::-1]
|
||||||
|
|
||||||
|
# XOR
|
||||||
|
key = ['B', 'A', 'Q', 'J', 'R', 'P', 'Z', 'P', 'A', 'H', 'U', 'T']
|
||||||
|
output = []
|
||||||
|
|
||||||
|
for i in range(len(code)):
|
||||||
|
xor_num = ord(code[i]) ^ ord(key[i % len(key)])
|
||||||
|
output.append(chr(xor_num))
|
||||||
|
|
||||||
|
code = ''.join(output)
|
||||||
|
|
||||||
|
return True, code
|
||||||
|
|
||||||
|
niraimarshal.niraicall_obfuscate = niraicall_obfuscate
|
||||||
|
|
||||||
|
class StridePackager(NiraiPackager):
|
||||||
|
HEADER = 'TTSTRIDE'
|
||||||
|
BASEDIR = '..' + os.sep
|
||||||
|
|
||||||
|
def __init__(self, outfile, configPath=None):
|
||||||
|
NiraiPackager.__init__(self, outfile)
|
||||||
|
self.__manglebase = self.get_mangle_base(self.BASEDIR)
|
||||||
|
self.add_panda3d_dirs()
|
||||||
|
self.add_default_lib()
|
||||||
|
self.globalConfigPath = configPath
|
||||||
|
|
||||||
|
def add_source_dir(self, dir):
|
||||||
|
self.add_directory(self.BASEDIR + dir, mangler=self.__mangler)
|
||||||
|
|
||||||
|
def add_data_file(self, file):
|
||||||
|
mb = self.get_mangle_base('data/')
|
||||||
|
self.add_file('data/%s.py' % file, mangler=lambda x: x[mb:])
|
||||||
|
|
||||||
|
def __mangler(self, name):
|
||||||
|
if name.endswith('AI') or name.endswith('UD') or name in ('ToontownAIRepository', 'ToontownUberRepository',
|
||||||
|
'ToontownInternalRepository', 'ServiceStart'):
|
||||||
|
if not 'NonRepeatableRandomSource' in name:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
return name[self.__manglebase:].strip('.')
|
||||||
|
|
||||||
|
def generate_niraidata(self):
|
||||||
|
print 'Generating niraidata'
|
||||||
|
if self.globalConfigPath is not None:
|
||||||
|
config = self.get_file_contents(self.globalConfigPath)
|
||||||
|
else:
|
||||||
|
config = self.get_file_contents('../dependencies/config/general.prc')
|
||||||
|
config += '\n\n' + self.get_file_contents('../dependencies/config/release/qa.prc')
|
||||||
|
|
||||||
|
config_iv = self.generate_key(16)
|
||||||
|
config_key = self.generate_key(16)
|
||||||
|
config = config_iv + config_key + aes.encrypt(config, config_key, config_iv)
|
||||||
|
niraidata = 'CONFIG = %r' % config
|
||||||
|
|
||||||
|
# DC
|
||||||
|
niraidata += '\nDC = %r' % self.get_file_contents('../dependencies/astron/dclass/stride.dc', True)
|
||||||
|
self.add_module('niraidata', niraidata, compile=True)
|
||||||
|
|
||||||
|
def process_modules(self):
|
||||||
|
# TODO: Compression
|
||||||
|
dg = Datagram()
|
||||||
|
dg.addUint32(len(self.modules))
|
||||||
|
for moduleName in self.modules:
|
||||||
|
data, size = self.modules[moduleName]
|
||||||
|
|
||||||
|
dg.addString(moduleName)
|
||||||
|
dg.addInt32(size)
|
||||||
|
dg.appendData(data)
|
||||||
|
|
||||||
|
data = dg.getMessage()
|
||||||
|
iv = self.generate_key(16)
|
||||||
|
key = self.generate_key(16)
|
||||||
|
fixed_key = ''.join(chr((i ^ (7 * i + 16)) % ((i + 5) * 3)) for i in xrange(16))
|
||||||
|
fixed_iv = ''.join(chr((i ^ (2 * i + 53)) % ((i + 9) * 6)) for i in xrange(16))
|
||||||
|
securekeyandiv = aes.encrypt(iv + key, fixed_key, fixed_iv)
|
||||||
|
return securekeyandiv + aes.encrypt(data, key, iv)
|
||||||
|
|
||||||
|
# Compile the engine
|
||||||
|
if args.compile_cxx:
|
||||||
|
compiler = NiraiCompiler('stride.exe', libs=set(glob.glob('libpandadna/libpandadna.dir/Release/*.obj')))
|
||||||
|
|
||||||
|
compiler.add_nirai_files()
|
||||||
|
compiler.add_source('src/stride.cxx')
|
||||||
|
|
||||||
|
compiler.run()
|
||||||
|
|
||||||
|
# Compile the game data
|
||||||
|
if args.make_nri:
|
||||||
|
pkg = StridePackager('built/TTSData.bin')
|
||||||
|
|
||||||
|
pkg.add_source_dir('otp')
|
||||||
|
pkg.add_source_dir('toontown')
|
||||||
|
|
||||||
|
pkg.add_data_file('NiraiStart')
|
||||||
|
|
||||||
|
pkg.generate_niraidata()
|
||||||
|
|
||||||
|
pkg.write_out()
|
||||||
|
|
||||||
|
if args.make_mfs:
|
||||||
|
os.chdir('../resources')
|
||||||
|
cmd = ''
|
||||||
|
for phasenum in ['3', '3.5', '4', '5', '5.5', '6', '7', '8', '9', '10', '11', '12', '13']:
|
||||||
|
print 'phase_%s' % (phasenum)
|
||||||
|
cmd = 'multify -cf ../build/built/resources/default/phase_%s.mf phase_%s' % (phasenum, phasenum)
|
||||||
|
p = subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
|
||||||
|
v = p.wait()
|
||||||
|
|
||||||
|
if v != 0:
|
||||||
|
print 'The following command returned non-zero value (%d): %s' % (v, cmd[:100] + '...')
|
||||||
|
sys.exit(1)
|
1
build/nirai/panda3d
Submodule
1
build/nirai/panda3d
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 510b6b2b3fef1674611e4666b45db949868a8869
|
1
build/nirai/python
Submodule
1
build/nirai/python
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d24aeac557165f986373fa6d59de128902b74112
|
1
build/nirai/src
Submodule
1
build/nirai/src
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 936977760bfac218f634533dd4d926ad7e8e2092
|
158
build/src/stride.cxx
Normal file
158
build/src/stride.cxx
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#include "nirai.h"
|
||||||
|
#include <datagram.h>
|
||||||
|
#include <datagramIterator.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <compress_string.h>
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) void initlibpandadna();
|
||||||
|
void init_libpandadna();
|
||||||
|
|
||||||
|
const char* header = "TTSTRIDE";
|
||||||
|
const int header_size = 8;
|
||||||
|
|
||||||
|
const int key_and_iv_size = 16;
|
||||||
|
|
||||||
|
int niraicall_onPreStart(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int niraicall_onLoadGameData()
|
||||||
|
{
|
||||||
|
fstream gd;
|
||||||
|
|
||||||
|
// Open the file
|
||||||
|
gd.open("TTSData.bin", ios_base::in | ios_base::binary);
|
||||||
|
if (!gd.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "Unable to open game file!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the header
|
||||||
|
char* read_header = new char[header_size];
|
||||||
|
gd.read(read_header, header_size);
|
||||||
|
|
||||||
|
if (memcmp(header, read_header, header_size))
|
||||||
|
{
|
||||||
|
std::cerr << "Invalid header" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] read_header;
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << gd.rdbuf();
|
||||||
|
gd.close();
|
||||||
|
|
||||||
|
std::string brawdata = ss.str();
|
||||||
|
|
||||||
|
// Decrypted the encrypted key and iv
|
||||||
|
std::string enckeyandiv = brawdata.substr(0, (key_and_iv_size * 2) + key_and_iv_size);
|
||||||
|
|
||||||
|
unsigned char* deckeyandiv = new unsigned char[enckeyandiv.size()];
|
||||||
|
unsigned char* fixed_key = new unsigned char[key_and_iv_size];
|
||||||
|
unsigned char* fixed_iv = new unsigned char[key_and_iv_size];
|
||||||
|
|
||||||
|
// Create fixed key and iv
|
||||||
|
|
||||||
|
for (int i = 0; i < key_and_iv_size; ++i)
|
||||||
|
fixed_key[i] = (i ^ (7 * i + 16)) % ((i + 5) * 3);
|
||||||
|
|
||||||
|
for (int i = 0; i < key_and_iv_size; ++i)
|
||||||
|
fixed_iv[i] = (i ^ (2 * i + 53)) % ((i + 9) * 6);
|
||||||
|
|
||||||
|
int deckeyandivsize = AES_decrypt((unsigned char*)enckeyandiv.c_str(), enckeyandiv.size(), fixed_key, fixed_iv, deckeyandiv);
|
||||||
|
|
||||||
|
delete[] fixed_key;
|
||||||
|
delete[] fixed_iv;
|
||||||
|
|
||||||
|
unsigned char* key = new unsigned char[key_and_iv_size];
|
||||||
|
unsigned char* iv = new unsigned char[key_and_iv_size];
|
||||||
|
|
||||||
|
// Move the decrypted key and iv into their subsequent char
|
||||||
|
|
||||||
|
for (int i = 0; i < key_and_iv_size; ++i)
|
||||||
|
iv[i] = deckeyandiv[i];
|
||||||
|
|
||||||
|
for (int i = 0; i < key_and_iv_size; ++i)
|
||||||
|
key[i] = deckeyandiv[i + key_and_iv_size];
|
||||||
|
|
||||||
|
delete[] deckeyandiv;
|
||||||
|
|
||||||
|
// Decrypt the game data
|
||||||
|
std::string rawdata = brawdata.substr((key_and_iv_size * 2) + key_and_iv_size);
|
||||||
|
unsigned char* decrypted_data = new unsigned char[rawdata.size()];
|
||||||
|
|
||||||
|
int decsize = AES_decrypt((unsigned char*)rawdata.c_str(), rawdata.size(), key, iv, decrypted_data); // Assumes no error
|
||||||
|
|
||||||
|
delete[] key;
|
||||||
|
delete[] iv;
|
||||||
|
|
||||||
|
// Read
|
||||||
|
|
||||||
|
// TODO: Compression
|
||||||
|
|
||||||
|
Datagram dg(decrypted_data, decsize);
|
||||||
|
DatagramIterator dgi(dg);
|
||||||
|
|
||||||
|
unsigned int num_modules = dgi.get_uint32();
|
||||||
|
_frozen* fzns = new _frozen[num_modules + 1];
|
||||||
|
std::string module, data;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < num_modules; ++i)
|
||||||
|
{
|
||||||
|
module = dgi.get_string();
|
||||||
|
size = dgi.get_int32();
|
||||||
|
data = dgi.extract_bytes(abs(size));
|
||||||
|
|
||||||
|
char* name = new char[module.size() + 1];
|
||||||
|
memcpy(name, module.c_str(), module.size());
|
||||||
|
memset(&name[module.size()], 0, 1);
|
||||||
|
|
||||||
|
unsigned char* code = new unsigned char[data.size()];
|
||||||
|
memcpy(code, data.c_str(), data.size());
|
||||||
|
|
||||||
|
_frozen fz;
|
||||||
|
fz.name = name;
|
||||||
|
fz.code = code;
|
||||||
|
fz.size = size;
|
||||||
|
|
||||||
|
memcpy(&fzns[i], &fz, sizeof(_frozen));
|
||||||
|
}
|
||||||
|
|
||||||
|
nassertd(dgi.get_remaining_size() == 0)
|
||||||
|
{
|
||||||
|
std::cerr << "Corrupted data!" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] decrypted_data;
|
||||||
|
|
||||||
|
memset(&fzns[num_modules], 0, sizeof(_frozen));
|
||||||
|
PyImport_FrozenModules = fzns;
|
||||||
|
|
||||||
|
// libpandadna
|
||||||
|
init_libpandadna();
|
||||||
|
initlibpandadna();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" PyObject* niraicall_deobfuscate(char* code, Py_ssize_t size)
|
||||||
|
{
|
||||||
|
std::string codestr(code, size);
|
||||||
|
|
||||||
|
const char key[12] = {'B', 'A', 'Q', 'J', 'R', 'P', 'Z', 'P', 'A', 'H', 'U', 'T'};
|
||||||
|
std::string output = codestr;
|
||||||
|
|
||||||
|
for (int i = 0; i < codestr.size(); i++)
|
||||||
|
output[i] = codestr[i] ^ key[i % (sizeof(key) / sizeof(char))];
|
||||||
|
|
||||||
|
std::reverse(output.begin(), output.end());
|
||||||
|
return PyString_FromStringAndSize(output.data(), size);
|
||||||
|
}
|
2
dependencies/.gitignore
vendored
Normal file
2
dependencies/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.json
|
||||||
|
backups/
|
3
dependencies/astron/.gitignore
vendored
Normal file
3
dependencies/astron/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Exclude the debug builds of Astron.
|
||||||
|
astrond_debug
|
||||||
|
astrond_debug.exe
|
BIN
dependencies/astron/astrond
vendored
Executable file
BIN
dependencies/astron/astrond
vendored
Executable file
Binary file not shown.
BIN
dependencies/astron/astrond.exe
vendored
Executable file
BIN
dependencies/astron/astrond.exe
vendored
Executable file
Binary file not shown.
42
dependencies/astron/config/clientagent-0.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-0.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #0
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7199
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000000000
|
||||||
|
max: 1000001000
|
42
dependencies/astron/config/clientagent-1.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-1.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #1
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7299
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000001001
|
||||||
|
max: 1000002000
|
42
dependencies/astron/config/clientagent-2.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-2.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #2
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7399
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000002001
|
||||||
|
max: 1000003000
|
42
dependencies/astron/config/clientagent-3.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-3.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #3
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7499
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000003001
|
||||||
|
max: 1000004000
|
42
dependencies/astron/config/clientagent-4.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-4.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #4
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7599
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000004001
|
||||||
|
max: 1000005000
|
42
dependencies/astron/config/clientagent-5.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-5.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #5
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7699
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000005001
|
||||||
|
max: 1000006000
|
42
dependencies/astron/config/clientagent-6.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-6.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #6
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7799
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000006001
|
||||||
|
max: 1000007000
|
42
dependencies/astron/config/clientagent-7.yml
vendored
Normal file
42
dependencies/astron/config/clientagent-7.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
daemon:
|
||||||
|
name: Client Agent #7
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
connect: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7899
|
||||||
|
version: "stride-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000007001
|
||||||
|
max: 1000008000
|
64
dependencies/astron/config/cluster.yml
vendored
Normal file
64
dependencies/astron/config/cluster.yml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
daemon:
|
||||||
|
name: Developer Cluster
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
bind: 127.0.0.1:7100
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7199
|
||||||
|
version: "tts-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000000000
|
||||||
|
max: 1000001000
|
||||||
|
|
||||||
|
- type: stateserver
|
||||||
|
control: 4002
|
||||||
|
|
||||||
|
- type: database
|
||||||
|
control: 4003
|
||||||
|
generate:
|
||||||
|
min: 100000000
|
||||||
|
max: 399999999
|
||||||
|
backend:
|
||||||
|
type: yaml
|
||||||
|
directory: ../databases/astrondb
|
||||||
|
|
||||||
|
- type: dbss
|
||||||
|
database: 4003
|
||||||
|
ranges:
|
||||||
|
- min: 100000000
|
||||||
|
max: 399999999
|
||||||
|
|
||||||
|
- type: eventlogger
|
||||||
|
bind: 127.0.0.1:7198
|
||||||
|
output: ../logs/events-%y%m%d_%H%M%S.log
|
7
dependencies/astron/config/eventlogger.yml
vendored
Normal file
7
dependencies/astron/config/eventlogger.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
daemon:
|
||||||
|
name: Event Logger
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: eventlogger
|
||||||
|
bind: 127.0.0.1:7198
|
||||||
|
output: ../logs/events-%y%m%d_%H%M%S.log
|
64
dependencies/astron/config/prod-test.yml
vendored
Normal file
64
dependencies/astron/config/prod-test.yml
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
daemon:
|
||||||
|
name: Developer Cluster
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 0.0.0.0:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
bind: 0.0.0.0:7100
|
||||||
|
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: clientagent
|
||||||
|
bind: 0.0.0.0:7199
|
||||||
|
version: "tts-dev"
|
||||||
|
client:
|
||||||
|
relocate: true
|
||||||
|
add_interest: enabled
|
||||||
|
channels:
|
||||||
|
min: 1000000000
|
||||||
|
max: 1000001000
|
||||||
|
|
||||||
|
- type: stateserver
|
||||||
|
control: 4002
|
||||||
|
|
||||||
|
- type: database
|
||||||
|
control: 4003
|
||||||
|
generate:
|
||||||
|
min: 100000000
|
||||||
|
max: 399999999
|
||||||
|
backend:
|
||||||
|
type: mongodb
|
||||||
|
server: mongodb://127.0.0.1:27017/stride
|
||||||
|
|
||||||
|
- type: dbss
|
||||||
|
database: 4003
|
||||||
|
ranges:
|
||||||
|
- min: 100000000
|
||||||
|
max: 399999999
|
||||||
|
|
||||||
|
- type: eventlogger
|
||||||
|
bind: 0.0.0.0:7198
|
||||||
|
output: ../logs/events-%y%m%d_%H%M%S.log
|
29
dependencies/astron/config/production-cluster.yml
vendored
Normal file
29
dependencies/astron/config/production-cluster.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
daemon:
|
||||||
|
name: Production Cluster
|
||||||
|
|
||||||
|
general:
|
||||||
|
eventlogger: 127.0.0.1:7198
|
||||||
|
dc_files:
|
||||||
|
- ../dclass/stride.dc
|
||||||
|
|
||||||
|
messagedirector:
|
||||||
|
bind: 127.0.0.1:7100
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- type: stateserver
|
||||||
|
control: 4002
|
||||||
|
|
||||||
|
- type: database
|
||||||
|
control: 4003
|
||||||
|
generate:
|
||||||
|
min: 100000000
|
||||||
|
max: 399999999
|
||||||
|
backend:
|
||||||
|
type: mongodb
|
||||||
|
server: mongodb://127.0.0.1:27017/stride
|
||||||
|
|
||||||
|
- type: dbss
|
||||||
|
database: 4003
|
||||||
|
ranges:
|
||||||
|
- min: 100000000
|
||||||
|
max: 399999999
|
20
dependencies/astron/config/uberdogs.yml
vendored
Executable file
20
dependencies/astron/config/uberdogs.yml
vendored
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
uberdogs:
|
||||||
|
- class: ClientServicesManager
|
||||||
|
id: 4665
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
- class: ChatAgent
|
||||||
|
id: 4681
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: FriendManager
|
||||||
|
id: 4501
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: TTSFriendsManager
|
||||||
|
id: 4666
|
||||||
|
anonymous: false
|
||||||
|
|
||||||
|
- class: GlobalPartyManager
|
||||||
|
id: 4477
|
||||||
|
anonymous: false
|
35
dependencies/astron/darwin/start-ai-server.sh
vendored
Normal file
35
dependencies/astron/darwin/start-ai-server.sh
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle
|
||||||
|
export DYLD_FRAMEWORK_PATH="Frameworks"
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="127.0.0.1:7100"
|
||||||
|
EVENTLOGGER_IP="127.0.0.1:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME
|
||||||
|
DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro}
|
||||||
|
read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-401000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride AI server..."
|
||||||
|
echo "District name: $DISTRICT_NAME"
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
ppython -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \
|
||||||
|
--district-name $DISTRICT_NAME
|
||||||
|
done
|
3
dependencies/astron/darwin/start-astron-cluster.sh
vendored
Normal file
3
dependencies/astron/darwin/start-astron-cluster.sh
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
./astrond --loglevel info config/cluster.yml
|
31
dependencies/astron/darwin/start-uberdog-server.sh
vendored
Normal file
31
dependencies/astron/darwin/start-uberdog-server.sh
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle
|
||||||
|
export DYLD_FRAMEWORK_PATH="Frameworks"
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="127.0.0.1:7100"
|
||||||
|
EVENTLOGGER_IP="127.0.0.1:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-1000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride UberDOG server..."
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
ppython -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP
|
||||||
|
done
|
1
dependencies/astron/databases/.gitignore
vendored
Normal file
1
dependencies/astron/databases/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.db
|
2
dependencies/astron/databases/astrondb/.gitignore
vendored
Executable file
2
dependencies/astron/databases/astrondb/.gitignore
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
3238
dependencies/astron/dclass/stride.dc
vendored
Normal file
3238
dependencies/astron/dclass/stride.dc
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
dependencies/astron/libeay32.dll
vendored
Executable file
BIN
dependencies/astron/libeay32.dll
vendored
Executable file
Binary file not shown.
32
dependencies/astron/linux/start-ai-server.sh
vendored
Normal file
32
dependencies/astron/linux/start-ai-server.sh
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="127.0.0.1:7100"
|
||||||
|
EVENTLOGGER_IP="127.0.0.1:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME
|
||||||
|
DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro}
|
||||||
|
read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-401000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride AI server..."
|
||||||
|
echo "District name: $DISTRICT_NAME"
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
/usr/bin/python2 -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \
|
||||||
|
--district-name $DISTRICT_NAME
|
||||||
|
done
|
3
dependencies/astron/linux/start-astron-cluster.sh
vendored
Normal file
3
dependencies/astron/linux/start-astron-cluster.sh
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
./astrond --loglevel info config/cluster.yml
|
28
dependencies/astron/linux/start-uberdog-server.sh
vendored
Normal file
28
dependencies/astron/linux/start-uberdog-server.sh
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="127.0.0.1:7100"
|
||||||
|
EVENTLOGGER_IP="127.0.0.1:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-1000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride UberDOG server..."
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
/usr/bin/python2 -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP
|
||||||
|
done
|
2
dependencies/astron/logs/.gitignore
vendored
Normal file
2
dependencies/astron/logs/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
BIN
dependencies/astron/msvcp120.dll
vendored
Executable file
BIN
dependencies/astron/msvcp120.dll
vendored
Executable file
Binary file not shown.
BIN
dependencies/astron/msvcr120.dll
vendored
Executable file
BIN
dependencies/astron/msvcr120.dll
vendored
Executable file
Binary file not shown.
32
dependencies/astron/prod/start-ai-server.sh
vendored
Executable file
32
dependencies/astron/prod/start-ai-server.sh
vendored
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="158.69.28.83:7100"
|
||||||
|
EVENTLOGGER_IP="158.69.28.83:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "District name (DEFAULT: Nuttyboro): " DISTRICT_NAME
|
||||||
|
DISTRICT_NAME=${DISTRICT_NAME:-Nuttyboro}
|
||||||
|
read -p "Base channel (DEFAULT: 401000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-401000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride AI server..."
|
||||||
|
echo "District name: $DISTRICT_NAME"
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
python -m toontown.ai.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP \
|
||||||
|
--district-name $DISTRICT_NAME
|
||||||
|
done
|
3
dependencies/astron/prod/start-astron-cluster.sh
vendored
Executable file
3
dependencies/astron/prod/start-astron-cluster.sh
vendored
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
./astrond --loglevel info config/prod-test.yml
|
28
dependencies/astron/prod/start-uberdog-server.sh
vendored
Executable file
28
dependencies/astron/prod/start-uberdog-server.sh
vendored
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
# Define some constants for our AI server:
|
||||||
|
MAX_CHANNELS=999999
|
||||||
|
STATESERVER=4002
|
||||||
|
ASTRON_IP="158.69.28.83:7100"
|
||||||
|
EVENTLOGGER_IP="158.69.28.83:7198"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Base channel (DEFAULT: 1000000): " BASE_CHANNEL
|
||||||
|
BASE_CHANNEL=${BASE_CHANNEL:-1000000}
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride UberDOG server..."
|
||||||
|
echo "Base channel: $BASE_CHANNEL"
|
||||||
|
echo "Max channels: $MAX_CHANNELS"
|
||||||
|
echo "State Server: $STATESERVER"
|
||||||
|
echo "Astron IP: $ASTRON_IP"
|
||||||
|
echo "Event Logger IP: $EVENTLOGGER_IP"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
while [ true ]
|
||||||
|
do
|
||||||
|
python -m toontown.uberdog.ServiceStart --base-channel $BASE_CHANNEL \
|
||||||
|
--max-channels $MAX_CHANNELS --stateserver $STATESERVER \
|
||||||
|
--astron-ip $ASTRON_IP --eventlogger-ip $EVENTLOGGER_IP
|
||||||
|
done
|
BIN
dependencies/astron/ssleay32.dll
vendored
Executable file
BIN
dependencies/astron/ssleay32.dll
vendored
Executable file
Binary file not shown.
112
dependencies/config/general.prc
vendored
Normal file
112
dependencies/config/general.prc
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
# Window settings:
|
||||||
|
window-title Toontown Stride
|
||||||
|
win-origin -1 -1
|
||||||
|
icon-filename phase_3/etc/icon.ico
|
||||||
|
cursor-filename phase_3/etc/toonmono.cur
|
||||||
|
|
||||||
|
# Audio:
|
||||||
|
audio-library-name p3openal_audio
|
||||||
|
video-library-name p3ffmpeg
|
||||||
|
|
||||||
|
# Graphics:
|
||||||
|
aux-display pandagl
|
||||||
|
aux-display pandadx9
|
||||||
|
aux-display p3tinydisplay
|
||||||
|
|
||||||
|
# Models:
|
||||||
|
model-cache-models #f
|
||||||
|
model-cache-textures #f
|
||||||
|
default-model-extension .bam
|
||||||
|
|
||||||
|
# Textures:
|
||||||
|
texture-anisotropic-degree 16
|
||||||
|
|
||||||
|
# Preferences:
|
||||||
|
preferences-filename user/preferences.json
|
||||||
|
|
||||||
|
# Backups:
|
||||||
|
backups-filepath dependencies/backups/
|
||||||
|
backups-extension .json
|
||||||
|
|
||||||
|
# Server:
|
||||||
|
server-timezone EST/EDT/-5
|
||||||
|
server-port 7199
|
||||||
|
account-bridge-filename astron/databases/account-bridge.db
|
||||||
|
|
||||||
|
# Performance:
|
||||||
|
texture-power-2 none
|
||||||
|
gl-check-errors #f
|
||||||
|
garbage-collect-states #t
|
||||||
|
|
||||||
|
# Egg object types:
|
||||||
|
egg-object-type-barrier <Scalar> collide-mask { 0x01 } <Collide> { Polyset descend }
|
||||||
|
egg-object-type-trigger <Scalar> collide-mask { 0x01 } <Collide> { Polyset descend intangible }
|
||||||
|
egg-object-type-sphere <Scalar> collide-mask { 0x01 } <Collide> { Sphere descend }
|
||||||
|
egg-object-type-trigger-sphere <Scalar> collide-mask { 0x01 } <Collide> { Sphere descend intangible }
|
||||||
|
egg-object-type-floor <Scalar> collide-mask { 0x02 } <Collide> { Polyset descend }
|
||||||
|
egg-object-type-dupefloor <Scalar> collide-mask { 0x02 } <Collide> { Polyset keep descend }
|
||||||
|
egg-object-type-camera-collide <Scalar> collide-mask { 0x04 } <Collide> { Polyset descend }
|
||||||
|
egg-object-type-camera-collide-sphere <Scalar> collide-mask { 0x04 } <Collide> { Sphere descend }
|
||||||
|
egg-object-type-camera-barrier <Scalar> collide-mask { 0x05 } <Collide> { Polyset descend }
|
||||||
|
egg-object-type-camera-barrier-sphere <Scalar> collide-mask { 0x05 } <Collide> { Sphere descend }
|
||||||
|
egg-object-type-model <Model> { 1 }
|
||||||
|
egg-object-type-dcs <DCS> { 1 }
|
||||||
|
|
||||||
|
# Safe zones:
|
||||||
|
want-safe-zones #t
|
||||||
|
want-toontown-central #t
|
||||||
|
want-donalds-dock #t
|
||||||
|
want-daisys-garden #t
|
||||||
|
want-minnies-melodyland #t
|
||||||
|
want-the-brrrgh #t
|
||||||
|
want-donalds-dreamland #t
|
||||||
|
want-goofy-speedway #t
|
||||||
|
want-outdoor-zone #t
|
||||||
|
want-golf-zone #t
|
||||||
|
|
||||||
|
# Safe zone settings:
|
||||||
|
want-treasure-planners #t
|
||||||
|
want-suit-planners #t
|
||||||
|
want-butterflies #t
|
||||||
|
|
||||||
|
# Trolley minigames:
|
||||||
|
want-minigames #t
|
||||||
|
|
||||||
|
# Picnic table board games:
|
||||||
|
want-game-tables #t
|
||||||
|
|
||||||
|
# Cog headquarters:
|
||||||
|
want-cog-headquarters #t
|
||||||
|
want-sellbot-headquarters #t
|
||||||
|
want-cashbot-headquarters #t
|
||||||
|
want-lawbot-headquarters #t
|
||||||
|
want-bossbot-headquarters #t
|
||||||
|
|
||||||
|
# Cog battles:
|
||||||
|
base-xp-multiplier 1.0
|
||||||
|
|
||||||
|
# SOS toons:
|
||||||
|
sos-card-reward 2
|
||||||
|
|
||||||
|
# CogDominiums (Field Offices):
|
||||||
|
want-emblems #t
|
||||||
|
cogdo-want-barrel-room #t
|
||||||
|
want-lawbot-cogdo #t
|
||||||
|
|
||||||
|
# Cog buildings:
|
||||||
|
want-cogbuildings #t
|
||||||
|
|
||||||
|
# Optional:
|
||||||
|
show-total-population #t
|
||||||
|
want-mat-all-tailors #t
|
||||||
|
estate-day-night #t
|
||||||
|
want-garden-game #f
|
||||||
|
want-language-selection #t
|
||||||
|
|
||||||
|
# Developer options:
|
||||||
|
want-dev #f
|
||||||
|
want-pstats 0
|
||||||
|
|
||||||
|
# Temporary:
|
||||||
|
smooth-lag 0.4
|
||||||
|
want-old-fireworks #t
|
1
dependencies/config/guieditor.prc
vendored
Normal file
1
dependencies/config/guieditor.prc
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
model-path ../resources/
|
64
dependencies/config/release/dev.prc
vendored
Normal file
64
dependencies/config/release/dev.prc
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# Distribution:
|
||||||
|
distribution dev
|
||||||
|
|
||||||
|
# Art assets:
|
||||||
|
vfs-mount resources/phase_3 /phase_3
|
||||||
|
vfs-mount resources/phase_3.5 /phase_3.5
|
||||||
|
vfs-mount resources/phase_4 /phase_4
|
||||||
|
vfs-mount resources/phase_5 /phase_5
|
||||||
|
vfs-mount resources/phase_5.5 /phase_5.5
|
||||||
|
vfs-mount resources/phase_6 /phase_6
|
||||||
|
vfs-mount resources/phase_7 /phase_7
|
||||||
|
vfs-mount resources/phase_8 /phase_8
|
||||||
|
vfs-mount resources/phase_9 /phase_9
|
||||||
|
vfs-mount resources/phase_10 /phase_10
|
||||||
|
vfs-mount resources/phase_11 /phase_11
|
||||||
|
vfs-mount resources/phase_12 /phase_12
|
||||||
|
vfs-mount resources/phase_13 /phase_13
|
||||||
|
vfs-mount resources/server /server
|
||||||
|
model-path /
|
||||||
|
|
||||||
|
# Server:
|
||||||
|
server-version tts-dev
|
||||||
|
min-access-level 700
|
||||||
|
accountdb-type developer
|
||||||
|
shard-low-pop 50
|
||||||
|
shard-mid-pop 100
|
||||||
|
|
||||||
|
# RPC:
|
||||||
|
want-rpc-server #f
|
||||||
|
rpc-server-endpoint http://localhost:8080/
|
||||||
|
|
||||||
|
# DClass file:
|
||||||
|
dc-file dependencies/astron/dclass/stride.dc
|
||||||
|
|
||||||
|
# Core features:
|
||||||
|
want-pets #t
|
||||||
|
want-parties #f
|
||||||
|
want-cogdominiums #t
|
||||||
|
want-lawbot-cogdo #t
|
||||||
|
want-anim-props #t
|
||||||
|
want-game-tables #t
|
||||||
|
want-find-four #t
|
||||||
|
want-chinese-checkers #t
|
||||||
|
want-checkers #t
|
||||||
|
want-house-types #t
|
||||||
|
want-gifting #t
|
||||||
|
want-top-toons #f
|
||||||
|
|
||||||
|
# Chat:
|
||||||
|
want-whitelist #f
|
||||||
|
want-sequence-list #f
|
||||||
|
|
||||||
|
# Developer options:
|
||||||
|
show-population #t
|
||||||
|
want-instant-parties #t
|
||||||
|
want-instant-delivery #t
|
||||||
|
cogdo-pop-factor 1.5
|
||||||
|
cogdo-ratio 0.5
|
||||||
|
default-directnotify-level info
|
||||||
|
|
||||||
|
# Crates:
|
||||||
|
dont-destroy-crate #t
|
||||||
|
get-key-reward-always #t
|
||||||
|
get-crate-reward-always #t
|
50
dependencies/config/release/qa.prc
vendored
Normal file
50
dependencies/config/release/qa.prc
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Distribution:
|
||||||
|
distribution qa
|
||||||
|
|
||||||
|
# Art assets:
|
||||||
|
model-path /
|
||||||
|
|
||||||
|
# Server:
|
||||||
|
server-version SERVER-VERSION-HERE
|
||||||
|
min-access-level 100
|
||||||
|
accountdb-type remote
|
||||||
|
shard-low-pop 50
|
||||||
|
shard-mid-pop 100
|
||||||
|
|
||||||
|
# RPC:
|
||||||
|
want-rpc-server #t
|
||||||
|
rpc-server-endpoint http://localhost:8080/
|
||||||
|
|
||||||
|
# DClass file is automatically wrapped into the niraidata.
|
||||||
|
|
||||||
|
# Core features:
|
||||||
|
want-pets #t
|
||||||
|
want-parties #f
|
||||||
|
want-cogdominiums #t
|
||||||
|
want-lawbot-cogdo #f
|
||||||
|
want-anim-props #t
|
||||||
|
want-game-tables #t
|
||||||
|
want-find-four #t
|
||||||
|
want-chinese-checkers #t
|
||||||
|
want-checkers #t
|
||||||
|
want-house-types #f
|
||||||
|
want-gifting #t
|
||||||
|
want-top-toons #f
|
||||||
|
want-emblems #f
|
||||||
|
|
||||||
|
# Chat:
|
||||||
|
want-whitelist #t
|
||||||
|
want-sequence-list #t
|
||||||
|
|
||||||
|
# Developer options:
|
||||||
|
show-population #t
|
||||||
|
want-instant-parties #f
|
||||||
|
want-instant-delivery #f
|
||||||
|
cogdo-pop-factor 1.5
|
||||||
|
cogdo-ratio 0.5
|
||||||
|
default-directnotify-level info
|
||||||
|
|
||||||
|
# Crates:
|
||||||
|
dont-destroy-crate #f
|
||||||
|
get-key-reward-always #f
|
||||||
|
get-crate-reward-always #f
|
BIN
dependencies/libpandadna.pyd
vendored
Executable file
BIN
dependencies/libpandadna.pyd
vendored
Executable file
Binary file not shown.
BIN
dependencies/libpandadna.so
vendored
Executable file
BIN
dependencies/libpandadna.so
vendored
Executable file
Binary file not shown.
22
dev/darwin/start-game-localhost.sh
Normal file
22
dev/darwin/start-game-localhost.sh
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle
|
||||||
|
export DYLD_FRAMEWORK_PATH="Frameworks"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Username: " ttsUsername
|
||||||
|
|
||||||
|
# Export the environment variables:
|
||||||
|
export ttsUsername=$ttsUsername
|
||||||
|
export ttsPassword="password"
|
||||||
|
export TTS_PLAYCOOKIE=$ttsUsername
|
||||||
|
export TTS_GAMESERVER="127.0.0.1"
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride..."
|
||||||
|
echo "Username: $ttsUsername"
|
||||||
|
echo "Gameserver: $TTS_GAMESERVER"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
ppython -m toontown.toonbase.ToontownStart
|
26
dev/darwin/start-game-remotedb.sh
Normal file
26
dev/darwin/start-game-remotedb.sh
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle
|
||||||
|
export DYLD_FRAMEWORK_PATH="Frameworks"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Username: " ttsUsername
|
||||||
|
read -s -p "Password: " ttsPassword
|
||||||
|
echo
|
||||||
|
read -p "Gameserver (DEFAULT: 167.114.28.238): " TTS_GAMESERVER
|
||||||
|
TTS_GAMESERVER=${TTS_GAMESERVER:-"167.114.28.238"}
|
||||||
|
|
||||||
|
# Export the environment variables:
|
||||||
|
export ttsUsername=$ttsUsername
|
||||||
|
export ttsPassword=$ttsPassword
|
||||||
|
export TTS_PLAYCOOKIE=$ttsUsername
|
||||||
|
export TTS_GAMESERVER=$TTS_GAMESERVER
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride..."
|
||||||
|
echo "Username: $ttsUsername"
|
||||||
|
echo "Gameserver: $TTS_GAMESERVER"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
ppython -m toontown.toonbase.ToontownStartRemoteDB
|
24
dev/darwin/start-game.sh
Normal file
24
dev/darwin/start-game.sh
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
export DYLD_LIBRARY_PATH=`pwd`/Libraries.bundle
|
||||||
|
export DYLD_FRAMEWORK_PATH="Frameworks"
|
||||||
|
|
||||||
|
# Get the user input:
|
||||||
|
read -p "Username: " ttsUsername
|
||||||
|
read -p "Gameserver (DEFAULT: 167.114.28.238): " TTS_GAMESERVER
|
||||||
|
TTS_GAMESERVER=${TTS_GAMESERVER:-"167.114.28.238"}
|
||||||
|
|
||||||
|
# Export the environment variables:
|
||||||
|
export ttsUsername=$ttsUsername
|
||||||
|
export ttsPassword="password"
|
||||||
|
export TTS_PLAYCOOKIE=$ttsUsername
|
||||||
|
export TTS_GAMESERVER=$TTS_GAMESERVER
|
||||||
|
|
||||||
|
echo "==============================="
|
||||||
|
echo "Starting Toontown Stride..."
|
||||||
|
echo "Username: $ttsUsername"
|
||||||
|
echo "Gameserver: $TTS_GAMESERVER"
|
||||||
|
echo "==============================="
|
||||||
|
|
||||||
|
ppython -m toontown.toonbase.ToontownStart
|
32
dev/tools/cleaner/cleanse.py
Normal file
32
dev/tools/cleaner/cleanse.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python2
|
||||||
|
# Cleanse the "../" directory of the following:
|
||||||
|
# - all files with a .pyc extension.
|
||||||
|
# - all "trash files" listed below:
|
||||||
|
# . parsetab.py - generated by the PLY module.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
extensions = ('.pyc',)
|
||||||
|
trashFiles = ('parsetab.py',)
|
||||||
|
|
||||||
|
print 'Changing to root directory...'
|
||||||
|
os.chdir('../../../')
|
||||||
|
|
||||||
|
print 'Scanning for garbage files...'
|
||||||
|
|
||||||
|
|
||||||
|
def delete(filepath):
|
||||||
|
print "Removing '%s'..." % filepath
|
||||||
|
os.unlink(filepath)
|
||||||
|
|
||||||
|
|
||||||
|
for root, folders, files in os.walk('.'):
|
||||||
|
for filename in files:
|
||||||
|
filepath = os.path.join(root, filename)
|
||||||
|
if os.path.splitext(filename)[1] in extensions:
|
||||||
|
delete(filepath)
|
||||||
|
elif filename in trashFiles:
|
||||||
|
delete(filepath)
|
||||||
|
|
||||||
|
print 'Done.'
|
9
dev/tools/doc/building/README.md
Normal file
9
dev/tools/doc/building/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Building
|
||||||
|
========
|
||||||
|
These documents outline everything you need to know for building a Toontown Stride client.
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
|
## Steps ##
|
||||||
|
|
||||||
|
TODO
|
9
dev/tools/doc/style-guide/README.md
Normal file
9
dev/tools/doc/style-guide/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Toontown Stride Style Guidelines
|
||||||
|
==================================
|
||||||
|
Code and documentation in the master and release branches of the Toontown Stride repositories must conform to these guidelines. Any code submitted that is not properly formated will be rejected, as it is best to keep a readable, and consistent style for future contributors to read, and understand the code. Don't, however, blindly follow these guidelines into writing unreadable code. Sometimes it is best to use your own judgement.
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
|
* [Git style guidelines](git-style.md)
|
||||||
|
* [Python style guidelines](python-style.md)
|
||||||
|
* [C++ style guidelines](cxx-style.md)
|
145
dev/tools/doc/style-guide/cxx-style.md
Normal file
145
dev/tools/doc/style-guide/cxx-style.md
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
C++ Style Guidelines
|
||||||
|
====================
|
||||||
|
For C++ programming, we use the [Oct 8, 2013 - Astron C++ Style Guide](https://github.com/Astron/Astron/blob/6a974ce247a364fdcd11d440db1cad0f1c2f6ba2/doc/style-guide/cxx-style.md "Oct 8, 2013 - Astron C++ Style Guide").
|
||||||
|
- - -
|
||||||
|
## Whitespace ##
|
||||||
|
Tabs shall be used to indent, spaces shall be used to align.
|
||||||
|
Source files should end with a newline.
|
||||||
|
|
||||||
|
## Variable Names ##
|
||||||
|
Variables shall have a descriptive lowercase name, where words are seperated by underscores.
|
||||||
|
Global variables shall start with g_
|
||||||
|
Member variables shall start with m_
|
||||||
|
|
||||||
|
Example: `field_count` or
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int m_my_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Function Names ##
|
||||||
|
Functions shall be named the same way as variables
|
||||||
|
|
||||||
|
## Class Names ##
|
||||||
|
Class names shall be in CamelCase
|
||||||
|
|
||||||
|
Example: `DistributedObject`
|
||||||
|
|
||||||
|
## Braces ##
|
||||||
|
Each brace shall be on it's own line, even if it's for an empty member:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
## Header Files ##
|
||||||
|
A class shall not have a header file if nothing else interacts with it or if nothing else will ever inherit from it.
|
||||||
|
|
||||||
|
## Typedefs ##
|
||||||
|
Typedefs shall have a descriptive name and end with _t
|
||||||
|
|
||||||
|
Example: `typedef unsigned int uint32_t`
|
||||||
|
|
||||||
|
## Usage of std::string (and when possible for stl types) ##
|
||||||
|
Whenever possible, have function parameters be of type `const std::string&` and not `std::string`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
void foo(const std::string &name)
|
||||||
|
{
|
||||||
|
std::cout << "Hello " << name << "!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
## Preprocessor Macros ##
|
||||||
|
Preproc macros shall be UPPER CASE and words shall be seperated by underlines.
|
||||||
|
|
||||||
|
Example: `#define CLIENT_HELLO 1`
|
||||||
|
|
||||||
|
## auto Keyword ##
|
||||||
|
The `auto` keyword shall be used to avoid typing out long iterator types, and only for that.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
std::list<DistributedObjects> my_objects;
|
||||||
|
auto it = my_objects.begin();
|
||||||
|
|
||||||
|
## Template specifiers ##
|
||||||
|
There shall be no space between the identifier and the <>
|
||||||
|
|
||||||
|
Good example: `std::list<channel_t> my_channels;`
|
||||||
|
|
||||||
|
Bad example: `std::list <channel_t> my_channels;`
|
||||||
|
|
||||||
|
## Access specifiers ##
|
||||||
|
Class/struct access specifiers shall be indented.
|
||||||
|
|
||||||
|
Good example:
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
Bad examples:
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
## Switches ##
|
||||||
|
Case statements inside switches shall be indented. If the case does not fall through, it shall have it's own scope.
|
||||||
|
The braces for the scope shall be on the same indentation level as the case statement itself.
|
||||||
|
At the end of the scope, a `break;` shall be placed on the same indentation level as the case statement.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
int temp = some_int;
|
||||||
|
switch(temp)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
//code
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//code
|
||||||
|
}
|
||||||
|
|
||||||
|
## Character Limit ##
|
||||||
|
Each line shall be no longer than 100 characters, each tab counting as 4 characters.
|
||||||
|
This is not a hard-limit, exceptions may be made.
|
||||||
|
|
||||||
|
## Initializer lists ##
|
||||||
|
The first variable inside of the initializer list shall be on the same line as the function header
|
||||||
|
if the character limit allows.
|
||||||
|
The following variables will be on the next line, with another level of indentation.
|
||||||
|
That line will continue until it hits the character limit, once that occurs a new line will be created,
|
||||||
|
with the same level of indenation.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int m_number;
|
||||||
|
int m_number2;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Foo() : m_number(0),
|
||||||
|
m_number2(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
30
dev/tools/doc/style-guide/git-style.md
Normal file
30
dev/tools/doc/style-guide/git-style.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Git Style Guidelines
|
||||||
|
====================
|
||||||
|
For Git, we try to follow a general pattern for commit messages and branch naming to make things organized and neat.
|
||||||
|
- - -
|
||||||
|
## Commit Messages ##
|
||||||
|
All commit messages should:
|
||||||
|
* Start with a capital letter.
|
||||||
|
* Never end in puncuation.
|
||||||
|
* Be in the present tense.
|
||||||
|
* Have a title less than 100 characters.
|
||||||
|
* End in a new line.
|
||||||
|
|
||||||
|
If a description is provided in the commit message, it should be separated from the title by a blank line. If the commit addresses an issue, its issue number should be referenced at the end of the commit message's description.
|
||||||
|
|
||||||
|
Whenever possible, commit messages should be prefixed with the directory name of which the commit modified the most, followed by a colon and a space.
|
||||||
|
|
||||||
|
For example: ```toon: ``` or ```tools: ``` or ```ai: ```
|
||||||
|
|
||||||
|
## Branch Naming ##
|
||||||
|
All branch names should:
|
||||||
|
* Be entirely lower case.
|
||||||
|
* Use **-** as a separator.
|
||||||
|
* Be categorized into one of the following groups:
|
||||||
|
* wip
|
||||||
|
* bugfix
|
||||||
|
* test
|
||||||
|
* enhancement
|
||||||
|
* feature
|
||||||
|
|
||||||
|
For example: ```feature/parties``` or ```bugfix/toontorial``` or ```enhancement/fix-memory-leak```
|
37
dev/tools/doc/style-guide/python-style.md
Normal file
37
dev/tools/doc/style-guide/python-style.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
Python Style Guidelines
|
||||||
|
=======================
|
||||||
|
For Python programming, we use a slightly modified version of the standard [PEP-8 Style Guide for Python Code](http://legacy.python.org/dev/peps/pep-0008 "PEP-8 Style Guide for Python Code"). Read below for our modifications.
|
||||||
|
- - -
|
||||||
|
## Code lay-out ##
|
||||||
|
### Indentation ###
|
||||||
|
The closing brace/bracket/parenthesis on multi-line constructs may either be directly at the end, as in:
|
||||||
|
|
||||||
|
my_list = [
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20]
|
||||||
|
result = some_function_that_takes_arguments(
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f')
|
||||||
|
|
||||||
|
or it may be by itself on the next line:
|
||||||
|
|
||||||
|
my_list = [
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20
|
||||||
|
]
|
||||||
|
result = some_function_that_takes_arguments(
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f'
|
||||||
|
)
|
||||||
|
|
||||||
|
### Tabs or Spaces? ###
|
||||||
|
**_Always_** use spaces.
|
||||||
|
|
||||||
|
### Maximum Line Length ###
|
||||||
|
**Docstrings and comments** should be restricted to _80 characters_. Anything else should be limited to _100 characters_.
|
||||||
|
|
||||||
|
## Naming Conventions ##
|
||||||
|
### Variables ###
|
||||||
|
Intentionally **unused** variables should be named "**_**". This will make common IDEs and editors ignore it.
|
||||||
|
|
||||||
|
## Strings ##
|
||||||
|
### Quotations ###
|
||||||
|
Use single quotations _(')_ unless there is one inside the string, in which case use double quotations _(")_.
|
19
dev/tools/findterm/findterm.py
Normal file
19
dev/tools/findterm/findterm.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def processFile(f,t):
|
||||||
|
data = open(f,'rb').read()
|
||||||
|
lines = data.replace('\r\n','\n').split('\n')
|
||||||
|
lines_found = []
|
||||||
|
for i,x in enumerate(lines):
|
||||||
|
if t in x:
|
||||||
|
lines_found.append(i+1)
|
||||||
|
|
||||||
|
return lines_found
|
||||||
|
|
||||||
|
term = raw_input('> ')
|
||||||
|
for x in glob.glob('../../../toontown/*/*.py'):
|
||||||
|
r = processFile(x,term)
|
||||||
|
if r:
|
||||||
|
print x,r
|
||||||
|
|
||||||
|
raw_input('*****')
|
19
dev/tools/findterm/findtermAI.py
Normal file
19
dev/tools/findterm/findtermAI.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def processFile(f,t):
|
||||||
|
data = open(f,'rb').read()
|
||||||
|
lines = data.replace('\r\n','\n').split('\n')
|
||||||
|
lines_found = []
|
||||||
|
for i,x in enumerate(lines):
|
||||||
|
if t in x:
|
||||||
|
lines_found.append(i+1)
|
||||||
|
|
||||||
|
return lines_found
|
||||||
|
|
||||||
|
term = raw_input('> ')
|
||||||
|
for x in glob.glob('../../../toontown/*/*AI.py'):
|
||||||
|
r = processFile(x,term)
|
||||||
|
if r:
|
||||||
|
print x,r
|
||||||
|
|
||||||
|
raw_input('*****')
|
19
dev/tools/findterm/findterm_otp.py
Normal file
19
dev/tools/findterm/findterm_otp.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import glob
|
||||||
|
|
||||||
|
def processFile(f,t):
|
||||||
|
data = open(f,'rb').read()
|
||||||
|
lines = data.replace('\r\n','\n').split('\n')
|
||||||
|
lines_found = []
|
||||||
|
for i,x in enumerate(lines):
|
||||||
|
if t in x:
|
||||||
|
lines_found.append(i+1)
|
||||||
|
|
||||||
|
return lines_found
|
||||||
|
|
||||||
|
term = raw_input('> ')
|
||||||
|
for x in glob.glob('../../../otp/*/*.py'):
|
||||||
|
r = processFile(x,term)
|
||||||
|
if r:
|
||||||
|
print x,r
|
||||||
|
|
||||||
|
raw_input('*****')
|
32
dev/tools/gui/EditorStart.py
Normal file
32
dev/tools/gui/EditorStart.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from direct.stdpy import threading
|
||||||
|
from direct.showbase.ShowBase import ShowBase
|
||||||
|
from panda3d.core import VirtualFileSystem
|
||||||
|
import __builtin__, wx, os
|
||||||
|
|
||||||
|
__builtin__.__dict__.update(__import__('panda3d.core', fromlist=['*']).__dict__)
|
||||||
|
|
||||||
|
loadPrcFile('dependencies/config/guieditor.prc')
|
||||||
|
loadPrcFile('dependencies/config/general.prc')
|
||||||
|
|
||||||
|
defaultText = """from direct.gui.DirectGui import *
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def inject(_):
|
||||||
|
code = textbox.GetValue()
|
||||||
|
exec(code, globals())
|
||||||
|
|
||||||
|
app = wx.App(redirect=False)
|
||||||
|
frame = wx.Frame(None, title="Injector", size=(640, 400), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX)
|
||||||
|
panel = wx.Panel(frame)
|
||||||
|
button = wx.Button(parent=panel, id=-1, label="Inject", size=(50, 20), pos=(295, 0))
|
||||||
|
textbox = wx.TextCtrl(parent=panel, id=-1, pos=(20, 22), size=(600, 340), style=wx.TE_MULTILINE)
|
||||||
|
|
||||||
|
frame.Bind(wx.EVT_BUTTON, inject, button)
|
||||||
|
frame.Show()
|
||||||
|
app.SetTopWindow(frame)
|
||||||
|
textbox.AppendText(defaultText)
|
||||||
|
threading.Thread(target=app.MainLoop).start()
|
||||||
|
|
||||||
|
__builtin__.base = ShowBase()
|
||||||
|
base.run()
|
15
dev/tools/gui/start-gui-editor.bat
Normal file
15
dev/tools/gui/start-gui-editor.bat
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
cd ../../../
|
||||||
|
|
||||||
|
title GUI Editor
|
||||||
|
set GUI=
|
||||||
|
|
||||||
|
:main
|
||||||
|
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" "dev/tools/gui/EditorStart.py"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
goto main
|
34
dev/tools/rpc/generate_key.py
Normal file
34
dev/tools/rpc/generate_key.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from Crypto import Random
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
# the block size for the cipher object; must be 16, 24, or 32 for AES
|
||||||
|
BLOCK_SIZE = 16
|
||||||
|
|
||||||
|
# the character used for padding--with a block cipher such as AES, the value
|
||||||
|
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
|
||||||
|
# used to ensure that your value is always a multiple of BLOCK_SIZE
|
||||||
|
PADDING = '{'
|
||||||
|
|
||||||
|
# one-liner to sufficiently pad the text to be encrypted
|
||||||
|
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
|
||||||
|
|
||||||
|
# one-liners to encrypt/encode and decrypt/decode a string
|
||||||
|
# encrypt with AES, encode with base64
|
||||||
|
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
|
||||||
|
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
|
||||||
|
|
||||||
|
# generate a random secret key
|
||||||
|
secret = os.urandom(BLOCK_SIZE)
|
||||||
|
|
||||||
|
# create a cipher object using the random secret
|
||||||
|
cipher = AES.new( secret, AES.MODE_CBC, secret )
|
||||||
|
|
||||||
|
# encode a string
|
||||||
|
encoded = EncodeAES(cipher, 'ttsrpclib691256')
|
||||||
|
print 'Encrypted string:', encoded
|
||||||
|
|
||||||
|
# decode the encoded string
|
||||||
|
decoded = DecodeAES(cipher, encoded)
|
||||||
|
print 'Decrypted string:', decoded
|
5
dev/tools/rpc/rpc-invasions.bat
Normal file
5
dev/tools/rpc/rpc-invasions.bat
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@echo off
|
||||||
|
cd ../../../
|
||||||
|
set /P serv=RPC Server:
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" "dev/tools/rpc/rpc-invasions.py" 6163636f756e7473 %serv%
|
||||||
|
pause
|
78
dev/tools/rpc/rpc-invasions.py
Normal file
78
dev/tools/rpc/rpc-invasions.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
from jsonrpclib import Server
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
import base64
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
RPC_SERVER_SECRET = sys.argv[1]
|
||||||
|
|
||||||
|
client = Server(sys.argv[2])
|
||||||
|
|
||||||
|
def generate_token(accessLevel):
|
||||||
|
"""
|
||||||
|
Generate an RPC server token with the given access level.
|
||||||
|
"""
|
||||||
|
token = {'timestamp': int(time.mktime(time.gmtime())), 'accesslevel': accessLevel}
|
||||||
|
data = json.dumps(token)
|
||||||
|
iv = os.urandom(AES.block_size)
|
||||||
|
cipher = AES.new(RPC_SERVER_SECRET, mode=AES.MODE_CBC, IV=iv)
|
||||||
|
data += '\x00' * (16 - (len(data)%AES.block_size))
|
||||||
|
token = cipher.encrypt(data)
|
||||||
|
return base64.b64encode(iv + token)
|
||||||
|
|
||||||
|
random.seed()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
res = client.ping(generate_token(700), 12345)
|
||||||
|
if res != 12345:
|
||||||
|
print "Is the server accessable?\n"
|
||||||
|
exit
|
||||||
|
|
||||||
|
# How many times a day is this script going to be called?
|
||||||
|
ChecksPerDay = 60.0*24.0 # Once a minute
|
||||||
|
InvasionsPerDay = 72.0 # How many invasions a day per district
|
||||||
|
BaseInvasionChance = InvasionsPerDay/ChecksPerDay
|
||||||
|
|
||||||
|
safeHarbor = {'Wacky Falls'}
|
||||||
|
superDistricts = {'Nuttyboro'}
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
shards = client.listShards(generate_token(700))
|
||||||
|
print shards
|
||||||
|
count = 0
|
||||||
|
for skey in shards:
|
||||||
|
shard = shards[skey]
|
||||||
|
if shard['invasion'] != None:
|
||||||
|
count = count + 1
|
||||||
|
for skey in shards:
|
||||||
|
shard = shards[skey]
|
||||||
|
if shard['invasion'] == None:
|
||||||
|
if shard['name'] in superDistricts:
|
||||||
|
typ = int(float(random.random()) * 4.0)
|
||||||
|
suit = int(float(random.random()) * 4.0) + 4 # Bias the cogs to be big
|
||||||
|
client.startInvasion(generate_token(700), int(skey), typ, suit, 0, 0)
|
||||||
|
count = count + 1
|
||||||
|
print 'Calling invasion for %s with %d,%d'%(shard['name'],typ,suit)
|
||||||
|
if count < 3:
|
||||||
|
for skey in shards:
|
||||||
|
shard = shards[skey]
|
||||||
|
if shard['invasion'] == None and not shard['name'] in safeHarbor:
|
||||||
|
r = random.random()
|
||||||
|
if r < BaseInvasionChance and not shard['name'] in superDistricts:
|
||||||
|
typ = int(float(random.random()) * 4.0)
|
||||||
|
suit = int(float(random.random()) * 8.0)
|
||||||
|
client.startInvasion(generate_token(700), int(skey), typ, suit, 0, 0)
|
||||||
|
print 'Calling invasion for %s with %d,%d'%(shard['name'],typ,suit)
|
||||||
|
print "tick..(was %d)\n"%(count)
|
||||||
|
time.sleep(60)
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
time.sleep(300)
|
5
dev/tools/rpc/rpctest.py
Normal file
5
dev/tools/rpc/rpctest.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import pyjsonrpc
|
||||||
|
|
||||||
|
http_client = pyjsonrpc.HttpClient(url = 'http://localhost:8080')
|
||||||
|
print 'Connected'
|
||||||
|
print http_client.ping('test')
|
223
dev/tools/rpc/write_rpc_doc.py
Normal file
223
dev/tools/rpc/write_rpc_doc.py
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
#!/usr/bin/env python2
|
||||||
|
import collections
|
||||||
|
import compiler
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryParser:
|
||||||
|
def __init__(self, filepath):
|
||||||
|
self.filepath = filepath
|
||||||
|
|
||||||
|
self.categories = {}
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
with open(self.filepath, 'r') as f:
|
||||||
|
for i, line in enumerate(f.readlines()):
|
||||||
|
line = line.strip()
|
||||||
|
if (not line.startswith('# --- ')) or (not line.endswith(' ---')):
|
||||||
|
continue
|
||||||
|
self.addCategory(i + 1, line[6:-4].title())
|
||||||
|
|
||||||
|
def addCategory(self, lineno, category):
|
||||||
|
self.categories[lineno] = category
|
||||||
|
|
||||||
|
def getCategory(self, lineno):
|
||||||
|
category = 'Unknown'
|
||||||
|
for k in sorted(self.categories.keys()):
|
||||||
|
if k > lineno:
|
||||||
|
break
|
||||||
|
category = self.categories[k]
|
||||||
|
return category
|
||||||
|
|
||||||
|
|
||||||
|
class MethodParser(CategoryParser):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
CategoryParser.__init__(self, filepath)
|
||||||
|
|
||||||
|
# Order matters, so store the method information in an OrderedDict:
|
||||||
|
self.methods = collections.OrderedDict()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
CategoryParser.parse(self)
|
||||||
|
|
||||||
|
# Get the root node:
|
||||||
|
node = compiler.parseFile(self.filepath).node
|
||||||
|
|
||||||
|
# Parse any class objects:
|
||||||
|
for child in node.getChildren():
|
||||||
|
if isinstance(child, compiler.ast.Class):
|
||||||
|
self.parseClass(child)
|
||||||
|
|
||||||
|
def parseClass(self, node):
|
||||||
|
# Skip past the class definition, and go into the body:
|
||||||
|
stmt = node.getChildNodes()[-1]
|
||||||
|
|
||||||
|
# Parse any function objects:
|
||||||
|
for child in stmt.getChildren():
|
||||||
|
if isinstance(child, compiler.ast.Function):
|
||||||
|
self.parseFunction(child)
|
||||||
|
|
||||||
|
def parseFunction(self, node):
|
||||||
|
# First, verify that this is an RPC method:
|
||||||
|
if not node.name.startswith('rpc_'):
|
||||||
|
# RPC methods are required to have their name begin with 'rpc_'.
|
||||||
|
return
|
||||||
|
name = node.name[4:]
|
||||||
|
if node.decorators is None:
|
||||||
|
# RPC methods are also required to utilize the @rpcmethod
|
||||||
|
# decorator.
|
||||||
|
return
|
||||||
|
for decorator in node.decorators:
|
||||||
|
if decorator.node.name != 'rpcmethod':
|
||||||
|
continue
|
||||||
|
accessLevel = 'Unknown'
|
||||||
|
for arg in decorator.args:
|
||||||
|
if arg.name != 'accessLevel':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Format the access level string:
|
||||||
|
accessLevel = ' '.join(arg.expr.name.split('_')).title()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# A docstring is also required:
|
||||||
|
if node.doc is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get rid of the indentation in our docstring so that we can have an
|
||||||
|
# easier time parsing it:
|
||||||
|
lines = node.doc.split('\n')
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
lines[i] = line.lstrip()
|
||||||
|
doc = '\n'.join(lines)
|
||||||
|
|
||||||
|
# Get the category in which this method is underneath:
|
||||||
|
category = self.getCategory(node.lineno)
|
||||||
|
|
||||||
|
# Store this method's information:
|
||||||
|
self.methods.setdefault(category, []).append((name, accessLevel, doc))
|
||||||
|
|
||||||
|
def getMethods(self):
|
||||||
|
return self.methods
|
||||||
|
|
||||||
|
|
||||||
|
class MediaWikiGenerator:
|
||||||
|
def __init__(self, methods):
|
||||||
|
self.methods = methods
|
||||||
|
|
||||||
|
self.content = ''
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
# Start on a clean slate:
|
||||||
|
self.content = ''
|
||||||
|
|
||||||
|
# Write the documentation header:
|
||||||
|
self.writeHeader()
|
||||||
|
|
||||||
|
# Write the categories and methods:
|
||||||
|
for category, methods in self.methods.items():
|
||||||
|
self.writeCategory(category)
|
||||||
|
for name, accessLevel, doc in methods:
|
||||||
|
self.writeMethod(name, accessLevel, doc)
|
||||||
|
|
||||||
|
# Write the documentation footer:
|
||||||
|
self.writeFooter()
|
||||||
|
|
||||||
|
return self.content
|
||||||
|
|
||||||
|
def writeHeader(self):
|
||||||
|
# Force a table of contents:
|
||||||
|
self.content += '__TOC__\n'
|
||||||
|
|
||||||
|
def writeCategory(self, category):
|
||||||
|
self.content += '= %s =\n' % category
|
||||||
|
|
||||||
|
def writeMethod(self, name, accessLevel, doc):
|
||||||
|
# First, add the method name and access level:
|
||||||
|
self.writeHeading(3, name + ' <sub>- <code>%s</code></sub>' % accessLevel)
|
||||||
|
|
||||||
|
# Split the docstring by the '\n\n' terminator:
|
||||||
|
doc = doc.split('\n\n')
|
||||||
|
|
||||||
|
# A summary is required, so let's assume it's first:
|
||||||
|
summary = doc[0][9:].strip()
|
||||||
|
self.writeBlockQuote(' '.join(summary.split('\n')))
|
||||||
|
|
||||||
|
# Let's do parameters next if we have them:
|
||||||
|
for entry in doc:
|
||||||
|
if entry.startswith('Parameters:'):
|
||||||
|
entry = entry[13:].strip()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
entry = None
|
||||||
|
if entry is not None:
|
||||||
|
parameters = []
|
||||||
|
for parameter in re.split('\\n\\[|\\n<', entry):
|
||||||
|
name, description = parameter.split(' = ', 1)
|
||||||
|
type, name = name.strip()[:-1].split(' ', 1)
|
||||||
|
description = ' '.join(description.split('\n'))
|
||||||
|
parameters.append((name, type, description))
|
||||||
|
self.writeParameters(parameters)
|
||||||
|
|
||||||
|
# Finally, we have an optional example response:
|
||||||
|
for entry in doc:
|
||||||
|
if entry.startswith('Example response:'):
|
||||||
|
entry = entry[18:].strip()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
entry = None
|
||||||
|
if entry is not None:
|
||||||
|
self.content += '{|\n'
|
||||||
|
self.content += '|-\n'
|
||||||
|
if (not entry.startswith('On success:')) or (
|
||||||
|
'On failure:' not in entry):
|
||||||
|
# Generate a single-row table:
|
||||||
|
self.content += '! Example Response\n'
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % entry
|
||||||
|
else:
|
||||||
|
# Generate a double-row table:
|
||||||
|
success, failure = entry[12:].split('On failure:', 1)
|
||||||
|
self.content += '! rowspan="2"|Example Response\n'
|
||||||
|
self.content += '| Success\n'
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % success.strip()
|
||||||
|
self.content += '|-\n'
|
||||||
|
self.content += '| Failure\n'
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % failure.strip()
|
||||||
|
self.content += '|}\n'
|
||||||
|
|
||||||
|
return self.content
|
||||||
|
|
||||||
|
def writeHeading(self, size, text):
|
||||||
|
self.content += '<h%d>%s</h%d>\n' % (size, text, size)
|
||||||
|
|
||||||
|
def writeBlockQuote(self, text):
|
||||||
|
self.content += '<blockquote><nowiki>%s</nowiki></blockquote>\n' % text
|
||||||
|
|
||||||
|
def writeParameters(self, parameters):
|
||||||
|
self.content += '{|\n'
|
||||||
|
self.content += '|-\n'
|
||||||
|
self.content += '! rowspan="%d"|Parameters\n' % (len(parameters) + 1)
|
||||||
|
self.content += '! Name\n'
|
||||||
|
self.content += '! Type\n'
|
||||||
|
self.content += '! Description\n'
|
||||||
|
for name, type, description in parameters:
|
||||||
|
self.content += '|-\n'
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % name
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % type
|
||||||
|
self.content += '| <nowiki>%s</nowiki>\n' % description
|
||||||
|
self.content += '|}\n'
|
||||||
|
|
||||||
|
def writeFooter(self):
|
||||||
|
# Let the reader know that this documentation was automatically
|
||||||
|
# generated:
|
||||||
|
self.content += '----\n'
|
||||||
|
self.content += ("''This document was automatically generated by the "
|
||||||
|
"<code>write_rpc_doc.py</code> utility.''\n")
|
||||||
|
|
||||||
|
|
||||||
|
parser = MethodParser('toontown/rpc/ToontownRPCHandler.py')
|
||||||
|
parser.parse()
|
||||||
|
generator = MediaWikiGenerator(parser.getMethods())
|
||||||
|
with open('wiki.txt', 'w') as f:
|
||||||
|
f.write(generator.generate())
|
3
dev/tools/whitelist/whitelist_tool.bat
Normal file
3
dev/tools/whitelist/whitelist_tool.bat
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@echo off
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" -m whitelist_tool
|
||||||
|
pause
|
85
dev/tools/whitelist/whitelist_tool.py
Normal file
85
dev/tools/whitelist/whitelist_tool.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import os
|
||||||
|
os.chdir('../../../')
|
||||||
|
|
||||||
|
from otp.chat import WhiteListData
|
||||||
|
|
||||||
|
def acceptWord():
|
||||||
|
word = raw_input('> ').rstrip().lower()
|
||||||
|
|
||||||
|
if word == 'exit()':
|
||||||
|
saveChanges()
|
||||||
|
return
|
||||||
|
|
||||||
|
if word.startswith('r '):
|
||||||
|
word = word.replace('r ', '')
|
||||||
|
if word not in LOCAL_LIST:
|
||||||
|
print 'Could not remove unknown word "%s" from the whitelist.' % word
|
||||||
|
else:
|
||||||
|
LOCAL_LIST.remove(word)
|
||||||
|
print 'Removed "%s" from the whitelist.' % word
|
||||||
|
elif word.startswith('m '):
|
||||||
|
merge = word.replace('m ', '')
|
||||||
|
|
||||||
|
if os.path.isfile(merge):
|
||||||
|
print 'Opening %s...' % merge
|
||||||
|
|
||||||
|
with open(merge) as file:
|
||||||
|
for line in file.readlines():
|
||||||
|
line = line.replace('\r', '').replace('\n', '').lower()
|
||||||
|
print 'Adding %s...' % line
|
||||||
|
LOCAL_LIST.append(line)
|
||||||
|
else:
|
||||||
|
print 'No file named %s!' % merge
|
||||||
|
elif word in LOCAL_LIST:
|
||||||
|
print 'The word "%s" is already whitelisted.' % word
|
||||||
|
else:
|
||||||
|
LOCAL_LIST.append(word)
|
||||||
|
print 'Added the word "%s" to the whitelist.' % word
|
||||||
|
|
||||||
|
acceptWord()
|
||||||
|
|
||||||
|
def removeDuplicates(list):
|
||||||
|
seen = set()
|
||||||
|
seen_add = seen.add
|
||||||
|
return [x for x in list if not (x in seen or seen_add(x))]
|
||||||
|
|
||||||
|
def saveChanges():
|
||||||
|
global LOCAL_LIST
|
||||||
|
print 'Saving the whitelist...'
|
||||||
|
|
||||||
|
with open('otp/chat/WhiteListData.py', 'w') as f:
|
||||||
|
f.write('WHITELIST = [\n')
|
||||||
|
|
||||||
|
LOCAL_LIST.sort()
|
||||||
|
LOCAL_LIST = removeDuplicates(LOCAL_LIST)
|
||||||
|
|
||||||
|
for word in LOCAL_LIST:
|
||||||
|
|
||||||
|
if '\\' in word:
|
||||||
|
print 'Word contains illegal characters: %s' % word
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
word.decode('ascii')
|
||||||
|
except:
|
||||||
|
print 'Word cannot be decoded in ASCII mode: %s' % word
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "'" in word:
|
||||||
|
f.write(' "%s",\n' % word)
|
||||||
|
else:
|
||||||
|
f.write(" '%s',\n" % word)
|
||||||
|
|
||||||
|
f.write(']')
|
||||||
|
|
||||||
|
print 'Your changes have been saved! Make sure to push your changes!'
|
||||||
|
|
||||||
|
LOCAL_LIST = WhiteListData.WHITELIST
|
||||||
|
|
||||||
|
print 'Welcome to the Toontown Stride Whitelist Tool!'
|
||||||
|
print 'Type any word you want to add to the whitelist.'
|
||||||
|
print 'If you wish to remove a word, type "r <word>".'
|
||||||
|
print 'If you wish to merge a file, type "m <file>".'
|
||||||
|
print 'When you are done and want to save your changes, type "exit()".'
|
||||||
|
|
||||||
|
|
||||||
|
acceptWord()
|
37
dev/win32/start-ai-server.bat
Executable file
37
dev/win32/start-ai-server.bat
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
rem Define some constants for our AI server:
|
||||||
|
set MAX_CHANNELS=999999
|
||||||
|
set STATESERVER=4002
|
||||||
|
set ASTRON_IP=127.0.0.1:7100
|
||||||
|
set EVENTLOGGER_IP=127.0.0.1:7198
|
||||||
|
|
||||||
|
rem Get the user input:
|
||||||
|
set /P DISTRICT_NAME="District name (DEFAULT: Nuttyboro): " || ^
|
||||||
|
set DISTRICT_NAME=Nuttyboro
|
||||||
|
set /P BASE_CHANNEL="Base channel (DEFAULT: 401000000): " || ^
|
||||||
|
set BASE_CHANNEL=401000000
|
||||||
|
|
||||||
|
echo ===============================
|
||||||
|
echo Starting Toontown Stride AI server...
|
||||||
|
echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe"
|
||||||
|
echo District name: %DISTRICT_NAME%
|
||||||
|
echo Base channel: %BASE_CHANNEL%
|
||||||
|
echo Max channels: %MAX_CHANNELS%
|
||||||
|
echo State Server: %STATESERVER%
|
||||||
|
echo Astron IP: %ASTRON_IP%
|
||||||
|
echo Event Logger IP: %EVENTLOGGER_IP%
|
||||||
|
echo ===============================
|
||||||
|
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
:main
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" ^
|
||||||
|
-m toontown.ai.ServiceStart ^
|
||||||
|
--base-channel %BASE_CHANNEL% ^
|
||||||
|
--max-channels %MAX_CHANNELS% ^
|
||||||
|
--stateserver %STATESERVER% ^
|
||||||
|
--astron-ip %ASTRON_IP% ^
|
||||||
|
--eventlogger-ip %EVENTLOGGER_IP% ^
|
||||||
|
--district-name "%DISTRICT_NAME%"
|
||||||
|
goto main
|
5
dev/win32/start-astron-cluster.bat
Executable file
5
dev/win32/start-astron-cluster.bat
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
@echo off
|
||||||
|
cd "../../dependencies/astron/"
|
||||||
|
|
||||||
|
astrond --loglevel info config/cluster.yml
|
||||||
|
pause
|
75
dev/win32/start-game.bat
Executable file
75
dev/win32/start-game.bat
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
title Toontown Stride Game Launcher
|
||||||
|
|
||||||
|
echo Choose your connection method!
|
||||||
|
echo.
|
||||||
|
echo #1 - Localhost
|
||||||
|
echo #2 - Dev Server
|
||||||
|
echo #3 - Custom
|
||||||
|
echo #4 - Local RemoteDB
|
||||||
|
echo #5 - Prod Server
|
||||||
|
echo.
|
||||||
|
|
||||||
|
:selection
|
||||||
|
|
||||||
|
set INPUT=-1
|
||||||
|
set /P INPUT=Selection:
|
||||||
|
|
||||||
|
if %INPUT%==1 (
|
||||||
|
set TTS_GAMESERVER=127.0.0.1
|
||||||
|
) else if %INPUT%==2 (
|
||||||
|
set TTS_GAMESERVER=167.114.220.172
|
||||||
|
) else if %INPUT%==4 (
|
||||||
|
set TTS_GAMESERVER=127.0.0.1
|
||||||
|
) else if %INPUT%==5 (
|
||||||
|
SET TTS_GAMESERVER=lw2.ez-webz.com:7198
|
||||||
|
) else if %INPUT%==3 (
|
||||||
|
echo.
|
||||||
|
set /P TTS_GAMESERVER=Gameserver:
|
||||||
|
) else (
|
||||||
|
goto selection
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
|
||||||
|
if %INPUT%==2 (
|
||||||
|
set /P ttsUsername="Username: "
|
||||||
|
set /P ttsPassword="Password: "
|
||||||
|
) else if %INPUT%==4 (
|
||||||
|
set /P ttsUsername="Username: "
|
||||||
|
set /P ttsPassword="Password: "
|
||||||
|
) else (
|
||||||
|
set /P TTS_PLAYCOOKIE=Username:
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo ===============================
|
||||||
|
echo Starting Toontown Stride...
|
||||||
|
echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe"
|
||||||
|
|
||||||
|
if %INPUT%==2 (
|
||||||
|
echo Username: %ttsUsername%
|
||||||
|
) else if %INPUT%==4 (
|
||||||
|
echo Username: %ttsUsername%
|
||||||
|
) else (
|
||||||
|
echo Username: %TTS_PLAYCOOKIE%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Gameserver: %TTS_GAMESERVER%
|
||||||
|
echo ===============================
|
||||||
|
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
:main
|
||||||
|
if %INPUT%==2 (
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStartRemoteDB
|
||||||
|
) else if %INPUT%==4 (
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStartRemoteDB
|
||||||
|
) else (
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" -m toontown.toonbase.ToontownStart
|
||||||
|
)
|
||||||
|
pause
|
||||||
|
|
||||||
|
goto main
|
33
dev/win32/start-uberdog-server.bat
Executable file
33
dev/win32/start-uberdog-server.bat
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
rem Define some constants for our UberDOG server:
|
||||||
|
set MAX_CHANNELS=999999
|
||||||
|
set STATESERVER=4002
|
||||||
|
set ASTRON_IP=127.0.0.1:7100
|
||||||
|
set EVENTLOGGER_IP=127.0.0.1:7198
|
||||||
|
|
||||||
|
rem Get the user input:
|
||||||
|
set /P BASE_CHANNEL="Base channel (DEFAULT: 1000000): " || ^
|
||||||
|
set BASE_CHANNEL=1000000
|
||||||
|
|
||||||
|
echo ===============================
|
||||||
|
echo Starting Toontown Stride UberDOG server...
|
||||||
|
echo ppython: "C:\Panda3D-1.10.0\python\ppython.exe"
|
||||||
|
echo Base channel: %BASE_CHANNEL%
|
||||||
|
echo Max channels: %MAX_CHANNELS%
|
||||||
|
echo State Server: %STATESERVER%
|
||||||
|
echo Astron IP: %ASTRON_IP%
|
||||||
|
echo Event Logger IP: %EVENTLOGGER_IP%
|
||||||
|
echo ===============================
|
||||||
|
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
:main
|
||||||
|
"C:\Panda3D-1.10.0\python\ppython.exe" ^
|
||||||
|
-m toontown.uberdog.ServiceStart ^
|
||||||
|
--base-channel %BASE_CHANNEL% ^
|
||||||
|
--max-channels %MAX_CHANNELS% ^
|
||||||
|
--stateserver %STATESERVER% ^
|
||||||
|
--astron-ip %ASTRON_IP% ^
|
||||||
|
--eventlogger-ip %EVENTLOGGER_IP%
|
||||||
|
goto main
|
0
otp/__init__.py
Executable file
0
otp/__init__.py
Executable file
133
otp/ai/AIBase.py
Executable file
133
otp/ai/AIBase.py
Executable file
|
@ -0,0 +1,133 @@
|
||||||
|
import gc
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from direct.directnotify.DirectNotifyGlobal import *
|
||||||
|
from direct.interval.IntervalManager import ivalMgr
|
||||||
|
from direct.showbase import EventManager
|
||||||
|
from direct.showbase import PythonUtil
|
||||||
|
from direct.showbase.BulletinBoardGlobal import *
|
||||||
|
from direct.showbase.EventManagerGlobal import *
|
||||||
|
from direct.showbase.JobManagerGlobal import *
|
||||||
|
from direct.showbase.MessengerGlobal import *
|
||||||
|
from direct.showbase.PythonUtil import *
|
||||||
|
from direct.task import Task
|
||||||
|
from direct.task.TaskManagerGlobal import *
|
||||||
|
from otp.otpbase import BackupManager
|
||||||
|
from panda3d.core import *
|
||||||
|
|
||||||
|
|
||||||
|
class AIBase:
|
||||||
|
notify = directNotify.newCategory('AIBase')
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = getConfigShowbase()
|
||||||
|
__builtins__['__dev__'] = self.config.GetBool('want-dev', 0)
|
||||||
|
if self.config.GetBool('use-vfs', 1):
|
||||||
|
vfs = VirtualFileSystem.getGlobalPtr()
|
||||||
|
else:
|
||||||
|
vfs = None
|
||||||
|
self.wantTk = self.config.GetBool('want-tk', 0)
|
||||||
|
self.AISleep = self.config.GetFloat('ai-sleep', 0.04)
|
||||||
|
self.AIRunningNetYield = self.config.GetBool('ai-running-net-yield', 0)
|
||||||
|
self.AIForceSleep = self.config.GetBool('ai-force-sleep', 0)
|
||||||
|
self.eventMgr = eventMgr
|
||||||
|
self.messenger = messenger
|
||||||
|
self.bboard = bulletinBoard
|
||||||
|
self.taskMgr = taskMgr
|
||||||
|
Task.TaskManager.taskTimerVerbose = self.config.GetBool('task-timer-verbose', 0)
|
||||||
|
Task.TaskManager.extendedExceptions = self.config.GetBool('extended-exceptions', 0)
|
||||||
|
self.sfxManagerList = None
|
||||||
|
self.musicManager = None
|
||||||
|
self.jobMgr = jobMgr
|
||||||
|
self.hidden = NodePath('hidden')
|
||||||
|
self.graphicsEngine = GraphicsEngine()
|
||||||
|
globalClock = ClockObject.getGlobalClock()
|
||||||
|
self.trueClock = TrueClock.getGlobalPtr()
|
||||||
|
globalClock.setRealTime(self.trueClock.getShortTime())
|
||||||
|
globalClock.setAverageFrameRateInterval(30.0)
|
||||||
|
globalClock.tick()
|
||||||
|
taskMgr.globalClock = globalClock
|
||||||
|
__builtins__['ostream'] = Notify.out()
|
||||||
|
__builtins__['globalClock'] = globalClock
|
||||||
|
__builtins__['vfs'] = vfs
|
||||||
|
__builtins__['hidden'] = self.hidden
|
||||||
|
PythonUtil.recordFunctorCreationStacks()
|
||||||
|
self.wantStats = self.config.GetBool('want-pstats', 0)
|
||||||
|
Task.TaskManager.pStatsTasks = self.config.GetBool('pstats-tasks', 0)
|
||||||
|
taskMgr.resumeFunc = PStatClient.resumeAfterPause
|
||||||
|
wantFakeTextures = self.config.GetBool('want-fake-textures-ai', 1)
|
||||||
|
if wantFakeTextures:
|
||||||
|
loadPrcFileData('aibase', 'textures-header-only 1')
|
||||||
|
self.wantPets = self.config.GetBool('want-pets', 1)
|
||||||
|
if self.wantPets:
|
||||||
|
from toontown.pets import PetConstants
|
||||||
|
self.petMoodTimescale = self.config.GetFloat('pet-mood-timescale', 1.0)
|
||||||
|
self.petMoodDriftPeriod = self.config.GetFloat('pet-mood-drift-period', PetConstants.MoodDriftPeriod)
|
||||||
|
self.petThinkPeriod = self.config.GetFloat('pet-think-period', PetConstants.ThinkPeriod)
|
||||||
|
self.petMovePeriod = self.config.GetFloat('pet-move-period', PetConstants.MovePeriod)
|
||||||
|
self.petPosBroadcastPeriod = self.config.GetFloat('pet-pos-broadcast-period', PetConstants.PosBroadcastPeriod)
|
||||||
|
self.wantBingo = self.config.GetBool('want-fish-bingo', 1)
|
||||||
|
self.wantKarts = self.config.GetBool('want-karts', 1)
|
||||||
|
self.backups = BackupManager.BackupManager(
|
||||||
|
filepath=self.config.GetString('backups-filepath', 'dependencies/backups/'),
|
||||||
|
extension=self.config.GetString('backups-extension', '.json'))
|
||||||
|
self.createStats()
|
||||||
|
self.restart()
|
||||||
|
|
||||||
|
def createStats(self, hostname = None, port = None):
|
||||||
|
if not self.wantStats:
|
||||||
|
return False
|
||||||
|
if PStatClient.isConnected():
|
||||||
|
PStatClient.disconnect()
|
||||||
|
if hostname is None:
|
||||||
|
hostname = ''
|
||||||
|
if port is None:
|
||||||
|
port = -1
|
||||||
|
PStatClient.connect(hostname, port)
|
||||||
|
return PStatClient.isConnected()
|
||||||
|
|
||||||
|
def __sleepCycleTask(self, task):
|
||||||
|
time.sleep(self.AISleep)
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def __resetPrevTransform(self, state):
|
||||||
|
PandaNode.resetAllPrevTransform()
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def __ivalLoop(self, state):
|
||||||
|
ivalMgr.step()
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def __igLoop(self, state):
|
||||||
|
self.graphicsEngine.renderFrame()
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
self.taskMgr.remove('ivalLoop')
|
||||||
|
self.taskMgr.remove('igLoop')
|
||||||
|
self.taskMgr.remove('aiSleep')
|
||||||
|
self.eventMgr.shutdown()
|
||||||
|
|
||||||
|
def restart(self):
|
||||||
|
self.shutdown()
|
||||||
|
self.taskMgr.add(self.__resetPrevTransform, 'resetPrevTransform', priority=-51)
|
||||||
|
self.taskMgr.add(self.__ivalLoop, 'ivalLoop', priority=20)
|
||||||
|
self.taskMgr.add(self.__igLoop, 'igLoop', priority=50)
|
||||||
|
if self.config.GetBool('garbage-collect-states', 1):
|
||||||
|
self.taskMgr.add(self.__garbageCollectStates, 'garbageCollectStates', priority=46)
|
||||||
|
if self.AISleep >= 0 and (not self.AIRunningNetYield or self.AIForceSleep):
|
||||||
|
self.taskMgr.add(self.__sleepCycleTask, 'aiSleep', priority=55)
|
||||||
|
self.eventMgr.restart()
|
||||||
|
|
||||||
|
def __garbageCollectStates(self, state):
|
||||||
|
TransformState.garbageCollect()
|
||||||
|
RenderState.garbageCollect()
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def getRepository(self):
|
||||||
|
return self.air
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.taskMgr.run()
|
32
otp/ai/AIBaseGlobal.py
Executable file
32
otp/ai/AIBaseGlobal.py
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
from AIBase import *
|
||||||
|
__builtins__['simbase'] = AIBase()
|
||||||
|
__builtins__['ostream'] = Notify.out()
|
||||||
|
__builtins__['run'] = simbase.run
|
||||||
|
__builtins__['taskMgr'] = simbase.taskMgr
|
||||||
|
__builtins__['jobMgr'] = simbase.jobMgr
|
||||||
|
__builtins__['eventMgr'] = simbase.eventMgr
|
||||||
|
__builtins__['messenger'] = simbase.messenger
|
||||||
|
__builtins__['bboard'] = simbase.bboard
|
||||||
|
__builtins__['config'] = simbase.config
|
||||||
|
__builtins__['directNotify'] = directNotify
|
||||||
|
from direct.showbase import Loader
|
||||||
|
simbase.loader = Loader.Loader(simbase)
|
||||||
|
__builtins__['loader'] = simbase.loader
|
||||||
|
directNotify.setDconfigLevels()
|
||||||
|
|
||||||
|
def inspect(anObject):
|
||||||
|
from direct.tkpanels import Inspector
|
||||||
|
Inspector.inspect(anObject)
|
||||||
|
|
||||||
|
|
||||||
|
__builtins__['inspect'] = inspect
|
||||||
|
taskMgr.finalInit()
|
||||||
|
|
||||||
|
# The VirtualFileSystem, which has already initialized, doesn't see the mount
|
||||||
|
# directives in the config(s) yet. We have to force it to load those manually:
|
||||||
|
from panda3d.core import VirtualFileSystem, ConfigVariableList, Filename
|
||||||
|
vfs = VirtualFileSystem.getGlobalPtr()
|
||||||
|
mounts = ConfigVariableList('vfs-mount')
|
||||||
|
for mount in mounts:
|
||||||
|
mountfile, mountpoint = (mount.split(' ', 2) + [None, None, None])[:2]
|
||||||
|
vfs.mount(Filename(mountfile), Filename(mountpoint), 0)
|
209
otp/ai/AIZoneData.py
Executable file
209
otp/ai/AIZoneData.py
Executable file
|
@ -0,0 +1,209 @@
|
||||||
|
from panda3d.core import *
|
||||||
|
from direct.distributed import ParentMgr
|
||||||
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||||
|
from direct.task import Task
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
import random
|
||||||
|
|
||||||
|
class AIZoneData:
|
||||||
|
notify = directNotify.newCategory('AIZoneData')
|
||||||
|
|
||||||
|
def __init__(self, air, parentId, zoneId):
|
||||||
|
self._air = air
|
||||||
|
self._parentId = parentId
|
||||||
|
self._zoneId = zoneId
|
||||||
|
self._data = self._air.getZoneDataStore().getDataForZone(self._parentId, self._zoneId)
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
del self._data
|
||||||
|
self._air.getZoneDataStore().releaseDataForZone(self._parentId, self._zoneId)
|
||||||
|
del self._zoneId
|
||||||
|
del self._parentId
|
||||||
|
del self._air
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self._data, attr)
|
||||||
|
|
||||||
|
|
||||||
|
class AIZoneDataObj:
|
||||||
|
notify = directNotify.newCategory('AIZoneDataObj')
|
||||||
|
DefaultCTravName = 'default'
|
||||||
|
|
||||||
|
def __init__(self, parentId, zoneId):
|
||||||
|
self._parentId = parentId
|
||||||
|
self._zoneId = zoneId
|
||||||
|
self._refCount = 0
|
||||||
|
self._collTravs = {}
|
||||||
|
self._collTravsStarted = set()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
output = str(self._collTravs)
|
||||||
|
output += '\n'
|
||||||
|
totalColliders = 0
|
||||||
|
totalTraversers = 0
|
||||||
|
for currCollTrav in self._collTravs.values():
|
||||||
|
totalTraversers += 1
|
||||||
|
totalColliders += currCollTrav.getNumColliders()
|
||||||
|
|
||||||
|
output += 'Num traversers: %s Num total colliders: %s' % (totalTraversers, totalColliders)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _incRefCount(self):
|
||||||
|
self._refCount += 1
|
||||||
|
|
||||||
|
def _decRefCount(self):
|
||||||
|
self._refCount -= 1
|
||||||
|
|
||||||
|
def _getRefCount(self):
|
||||||
|
return self._refCount
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
for name in list(self._collTravsStarted):
|
||||||
|
self.stopCollTrav(cTravName=name)
|
||||||
|
|
||||||
|
del self._collTravsStarted
|
||||||
|
del self._collTravs
|
||||||
|
if hasattr(self, '_nonCollidableParent'):
|
||||||
|
self._nonCollidableParent.removeNode()
|
||||||
|
del self._nonCollidableParent
|
||||||
|
if hasattr(self, '_render'):
|
||||||
|
self._render.removeNode()
|
||||||
|
del self._render
|
||||||
|
if hasattr(self, '_parentMgr'):
|
||||||
|
self._parentMgr.destroy()
|
||||||
|
del self._parentMgr
|
||||||
|
del self._zoneId
|
||||||
|
del self._parentId
|
||||||
|
|
||||||
|
def getLocation(self):
|
||||||
|
return (self._parentId, self._zoneId)
|
||||||
|
|
||||||
|
def getRender(self):
|
||||||
|
if not hasattr(self, '_render'):
|
||||||
|
self._render = NodePath('render-%s-%s' % (self._parentId, self._zoneId))
|
||||||
|
return self._render
|
||||||
|
|
||||||
|
def getNonCollidableParent(self):
|
||||||
|
if not hasattr(self, '_nonCollidableParent'):
|
||||||
|
render = self.getRender()
|
||||||
|
self._nonCollidableParent = render.attachNewNode('nonCollidables')
|
||||||
|
return self._nonCollidableParent
|
||||||
|
|
||||||
|
def getParentMgr(self):
|
||||||
|
if not hasattr(self, '_parentMgr'):
|
||||||
|
self._parentMgr = ParentMgr.ParentMgr()
|
||||||
|
self._parentMgr.registerParent(OTPGlobals.SPHidden, hidden)
|
||||||
|
self._parentMgr.registerParent(OTPGlobals.SPRender, self.getRender())
|
||||||
|
return self._parentMgr
|
||||||
|
|
||||||
|
def hasCollTrav(self, name = None):
|
||||||
|
if name is None:
|
||||||
|
name = AIZoneDataObj.DefaultCTravName
|
||||||
|
return name in self._collTravs
|
||||||
|
|
||||||
|
def getCollTrav(self, name = None):
|
||||||
|
if name is None:
|
||||||
|
name = AIZoneDataObj.DefaultCTravName
|
||||||
|
if name not in self._collTravs:
|
||||||
|
self._collTravs[name] = CollisionTraverser('cTrav-%s-%s-%s' % (name, self._parentId, self._zoneId))
|
||||||
|
return self._collTravs[name]
|
||||||
|
|
||||||
|
def removeCollTrav(self, name):
|
||||||
|
if name in self._collTravs:
|
||||||
|
del self._collTravs[name]
|
||||||
|
|
||||||
|
def _getCTravTaskName(self, name = None):
|
||||||
|
if name is None:
|
||||||
|
name = AIZoneDataObj.DefaultCTravName
|
||||||
|
return 'collTrav-%s-%s-%s' % (name, self._parentId, self._zoneId)
|
||||||
|
|
||||||
|
def _doCollisions(self, task = None, topNode = None, cTravName = None):
|
||||||
|
render = self.getRender()
|
||||||
|
curTime = globalClock.getFrameTime()
|
||||||
|
render.setTag('lastTraverseTime', str(curTime))
|
||||||
|
if topNode is not None:
|
||||||
|
if not render.isAncestorOf(topNode):
|
||||||
|
self.notify.warning('invalid topNode for collision traversal in %s: %s' % (self.getLocation(), topNode))
|
||||||
|
else:
|
||||||
|
topNode = render
|
||||||
|
if cTravName is None:
|
||||||
|
cTravName = AIZoneDataObj.DefaultCTravName
|
||||||
|
collTrav = self._collTravs[cTravName]
|
||||||
|
messenger.send('preColl-' + collTrav.getName())
|
||||||
|
collTrav.traverse(topNode)
|
||||||
|
messenger.send('postColl-' + collTrav.getName())
|
||||||
|
return Task.cont
|
||||||
|
|
||||||
|
def doCollTrav(self, topNode = None, cTravName = None):
|
||||||
|
self.getCollTrav(cTravName)
|
||||||
|
self._doCollisions(topNode=topNode, cTravName=cTravName)
|
||||||
|
|
||||||
|
def startCollTrav(self, respectPrevTransform = 1, cTravName = None):
|
||||||
|
if cTravName is None:
|
||||||
|
cTravName = AIZoneDataObj.DefaultCTravName
|
||||||
|
if cTravName not in self._collTravsStarted:
|
||||||
|
self.getCollTrav(name=cTravName)
|
||||||
|
taskMgr.add(self._doCollisions, self._getCTravTaskName(name=cTravName), priority=OTPGlobals.AICollisionPriority, extraArgs=[self._zoneId])
|
||||||
|
self._collTravsStarted.add(cTravName)
|
||||||
|
self.setRespectPrevTransform(respectPrevTransform, cTravName=cTravName)
|
||||||
|
return
|
||||||
|
|
||||||
|
def stopCollTrav(self, cTravName = None):
|
||||||
|
if cTravName is None:
|
||||||
|
cTravName = AIZoneDataObj.DefaultCTravName
|
||||||
|
self.notify.debug('stopCollTrav(%s, %s, %s)' % (cTravName, self._parentId, self._zoneId))
|
||||||
|
if cTravName in self._collTravsStarted:
|
||||||
|
self.notify.info('removing %s collision traversal for (%s, %s)' % (cTravName, self._parentId, self._zoneId))
|
||||||
|
taskMgr.remove(self._getCTravTaskName(name=cTravName))
|
||||||
|
self._collTravsStarted.remove(cTravName)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setRespectPrevTransform(self, flag, cTravName = None):
|
||||||
|
if cTravName is None:
|
||||||
|
cTravName = AIZoneDataObj.DefaultCTravName
|
||||||
|
self._collTravs[cTravName].setRespectPrevTransform(flag)
|
||||||
|
return
|
||||||
|
|
||||||
|
def getRespectPrevTransform(self, cTravName = None):
|
||||||
|
if cTravName is None:
|
||||||
|
cTravName = AIZoneDataObj.DefaultCTravName
|
||||||
|
return self._collTravs[cTravName].getRespectPrevTransform()
|
||||||
|
|
||||||
|
|
||||||
|
class AIZoneDataStore:
|
||||||
|
notify = directNotify.newCategory('AIZoneDataStore')
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._zone2data = {}
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
for zone, data in self._zone2data.items():
|
||||||
|
data.destroy()
|
||||||
|
|
||||||
|
del self._zone2data
|
||||||
|
|
||||||
|
def hasDataForZone(self, parentId, zoneId):
|
||||||
|
key = (parentId, zoneId)
|
||||||
|
return key in self._zone2data
|
||||||
|
|
||||||
|
def getDataForZone(self, parentId, zoneId):
|
||||||
|
key = (parentId, zoneId)
|
||||||
|
if key not in self._zone2data:
|
||||||
|
self._zone2data[key] = AIZoneDataObj(parentId, zoneId)
|
||||||
|
self.printStats()
|
||||||
|
data = self._zone2data[key]
|
||||||
|
data._incRefCount()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def releaseDataForZone(self, parentId, zoneId):
|
||||||
|
key = (parentId, zoneId)
|
||||||
|
data = self._zone2data[key]
|
||||||
|
data._decRefCount()
|
||||||
|
refCount = data._getRefCount()
|
||||||
|
if refCount == 0:
|
||||||
|
del self._zone2data[key]
|
||||||
|
data.destroy()
|
||||||
|
self.printStats()
|
||||||
|
|
||||||
|
def printStats(self):
|
||||||
|
self.notify.debug('%s zones have zone data allocated' % len(self._zone2data))
|
142
otp/ai/BanManagerAI.py
Executable file
142
otp/ai/BanManagerAI.py
Executable file
|
@ -0,0 +1,142 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from toontown.uberdog.ClientServicesManagerUD import executeHttpRequest
|
||||||
|
import time
|
||||||
|
from direct.fsm.FSM import FSM
|
||||||
|
from direct.distributed.PyDatagram import PyDatagram
|
||||||
|
from direct.distributed.MsgTypes import *
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from direct.showbase.DirectObject import DirectObject
|
||||||
|
|
||||||
|
|
||||||
|
class BanFSM(FSM):
|
||||||
|
|
||||||
|
def __init__(self, air, avId, comment, duration, banner):
|
||||||
|
FSM.__init__(self, 'banFSM-%s' % avId)
|
||||||
|
self.air = air
|
||||||
|
self.avId = avId
|
||||||
|
|
||||||
|
# Needed variables for the actual banning.
|
||||||
|
self.comment = comment
|
||||||
|
self.duration = duration
|
||||||
|
self.DISLid = None
|
||||||
|
self.accountId = None
|
||||||
|
self.avName = None
|
||||||
|
self.banner = banner
|
||||||
|
|
||||||
|
def performBan(self, duration):
|
||||||
|
executeHttpRequest('ban', username=self.accountId, start=int(time.time()),
|
||||||
|
duration=duration, reason=self.comment, bannedby=self.banner)
|
||||||
|
|
||||||
|
def ejectPlayer(self):
|
||||||
|
av = self.air.doId2do.get(self.avId)
|
||||||
|
if not av:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send the client a 'CLIENTAGENT_EJECT' with the players name.
|
||||||
|
datagram = PyDatagram()
|
||||||
|
datagram.addServerHeader(
|
||||||
|
av.GetPuppetConnectionChannel(self.avId),
|
||||||
|
self.air.ourChannel, CLIENTAGENT_EJECT)
|
||||||
|
datagram.addUint16(152)
|
||||||
|
datagram.addString(self.avName)
|
||||||
|
simbase.air.send(datagram)
|
||||||
|
|
||||||
|
def dbCallback(self, dclass, fields):
|
||||||
|
if dclass != simbase.air.dclassesByName['AccountAI']:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.accountId = fields.get('ACCOUNT_ID')
|
||||||
|
|
||||||
|
if not self.accountId:
|
||||||
|
return
|
||||||
|
|
||||||
|
if simbase.config.GetBool('want-bans', True):
|
||||||
|
self.performBan(self.duration)
|
||||||
|
self.duration = None
|
||||||
|
|
||||||
|
def getAvatarDetails(self):
|
||||||
|
av = self.air.doId2do.get(self.avId)
|
||||||
|
if not av:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.DISLid = av.getDISLid()
|
||||||
|
self.avName = av.getName()
|
||||||
|
|
||||||
|
def log(self):
|
||||||
|
simbase.air.writeServerEvent('ban', self.accountId, self.comment)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.air = None
|
||||||
|
self.avId = None
|
||||||
|
|
||||||
|
self.DISLid = None
|
||||||
|
self.avName = None
|
||||||
|
self.accountId = None
|
||||||
|
self.comment = None
|
||||||
|
self.duration = None
|
||||||
|
self = None
|
||||||
|
|
||||||
|
def enterStart(self):
|
||||||
|
self.getAvatarDetails()
|
||||||
|
self.air.dbInterface.queryObject(self.air.dbId, self.DISLid,
|
||||||
|
self.dbCallback)
|
||||||
|
self.ejectPlayer()
|
||||||
|
|
||||||
|
def exitStart(self):
|
||||||
|
self.log()
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
def enterOff(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exitOff(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BanManagerAI(DirectObject):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('BanManagerAI')
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
self.air = air
|
||||||
|
self.banFSMs = {}
|
||||||
|
|
||||||
|
def ban(self, avId, duration, comment, banner):
|
||||||
|
self.banFSMs[avId] = BanFSM(self.air, avId, comment, duration, banner)
|
||||||
|
self.banFSMs[avId].request('Start')
|
||||||
|
|
||||||
|
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.banDone, [avId])
|
||||||
|
|
||||||
|
def banDone(self, avId):
|
||||||
|
self.banFSMs[avId].request('Off')
|
||||||
|
self.banFSMs[avId] = None
|
||||||
|
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_MODERATOR, types=[str])
|
||||||
|
def kick(reason='No reason specified'):
|
||||||
|
"""
|
||||||
|
Kick the target from the game server.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
datagram = PyDatagram()
|
||||||
|
datagram.addServerHeader(
|
||||||
|
target.GetPuppetConnectionChannel(target.doId),
|
||||||
|
simbase.air.ourChannel, CLIENTAGENT_EJECT)
|
||||||
|
datagram.addUint16(155)
|
||||||
|
datagram.addString('You were kicked by a moderator for the following reason: %s' % reason)
|
||||||
|
simbase.air.send(datagram)
|
||||||
|
return "Kicked %s from the game server!" % target.getName()
|
||||||
|
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_MODERATOR, types=[str, int])
|
||||||
|
def ban(reason, duration):
|
||||||
|
"""
|
||||||
|
Ban the target from the game server.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
if target == spellbook.getInvoker():
|
||||||
|
return "You can't ban yourself!"
|
||||||
|
if reason not in ('hacking', 'language', 'other'):
|
||||||
|
return "'%s' is not a valid reason." % reason
|
||||||
|
banner = spellbook.getInvoker().DISLid
|
||||||
|
simbase.air.banManager.ban(target.doId, duration, reason, banner)
|
||||||
|
return "Banned %s from the game server!" % target.getName()
|
83
otp/ai/Barrier.py
Executable file
83
otp/ai/Barrier.py
Executable file
|
@ -0,0 +1,83 @@
|
||||||
|
from otp.ai.AIBase import *
|
||||||
|
from direct.task import Task
|
||||||
|
from direct.showbase import DirectObject
|
||||||
|
import random
|
||||||
|
|
||||||
|
class Barrier(DirectObject.DirectObject):
|
||||||
|
notify = directNotify.newCategory('Barrier')
|
||||||
|
|
||||||
|
def __init__(self, name, uniqueName, avIdList, timeout, clearedFunc = None, timeoutFunc = None, doneFunc = None):
|
||||||
|
self.name = name
|
||||||
|
self.uniqueName = uniqueName + '-Barrier'
|
||||||
|
self.avIdList = avIdList[:]
|
||||||
|
self.pendingAvatars = self.avIdList[:]
|
||||||
|
self.timeout = timeout
|
||||||
|
self.clearedFunc = clearedFunc
|
||||||
|
self.timeoutFunc = timeoutFunc
|
||||||
|
self.doneFunc = doneFunc
|
||||||
|
if len(self.pendingAvatars) == 0:
|
||||||
|
self.notify.debug('%s: barrier with empty list' % self.uniqueName)
|
||||||
|
self.active = 0
|
||||||
|
if self.clearedFunc:
|
||||||
|
self.clearedFunc()
|
||||||
|
if self.doneFunc:
|
||||||
|
self.doneFunc(self.avIdList)
|
||||||
|
return
|
||||||
|
self.taskName = self.uniqueName + '-Timeout'
|
||||||
|
origTaskName = self.taskName
|
||||||
|
while taskMgr.hasTaskNamed(self.taskName):
|
||||||
|
self.taskName = origTaskName + '-' + str(random.randint(0, 10000))
|
||||||
|
|
||||||
|
taskMgr.doMethodLater(self.timeout, self.__timerExpired, self.taskName)
|
||||||
|
for avId in self.avIdList:
|
||||||
|
event = simbase.air.getAvatarExitEvent(avId)
|
||||||
|
self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId])
|
||||||
|
|
||||||
|
self.notify.debug('%s: expecting responses from %s within %s seconds' % (self.uniqueName, self.avIdList, self.timeout))
|
||||||
|
self.active = 1
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
if self.active:
|
||||||
|
taskMgr.remove(self.taskName)
|
||||||
|
self.active = 0
|
||||||
|
self.ignoreAll()
|
||||||
|
|
||||||
|
def clear(self, avId):
|
||||||
|
if avId not in self.pendingAvatars:
|
||||||
|
self.notify.warning('%s: tried to clear %s, who was not listed.' % (self.uniqueName, avId))
|
||||||
|
return
|
||||||
|
self.notify.debug('%s: clearing avatar %s' % (self.uniqueName, avId))
|
||||||
|
self.pendingAvatars.remove(avId)
|
||||||
|
if len(self.pendingAvatars) == 0:
|
||||||
|
self.notify.debug('%s: barrier cleared by %s' % (self.uniqueName, self.avIdList))
|
||||||
|
self.cleanup()
|
||||||
|
if self.clearedFunc:
|
||||||
|
self.clearedFunc()
|
||||||
|
if self.doneFunc:
|
||||||
|
self.doneFunc(self.avIdList)
|
||||||
|
|
||||||
|
def isActive(self):
|
||||||
|
return self.active
|
||||||
|
|
||||||
|
def getPendingAvatars(self):
|
||||||
|
return self.pendingAvatars[:]
|
||||||
|
|
||||||
|
def __timerExpired(self, task):
|
||||||
|
self.notify.warning('%s: timeout expired; responses not received from %s' % (self.uniqueName, self.pendingAvatars))
|
||||||
|
self.cleanup()
|
||||||
|
if self.timeoutFunc:
|
||||||
|
self.timeoutFunc(self.pendingAvatars[:])
|
||||||
|
if self.doneFunc:
|
||||||
|
clearedAvIds = self.avIdList[:]
|
||||||
|
for avId in self.pendingAvatars:
|
||||||
|
clearedAvIds.remove(avId)
|
||||||
|
|
||||||
|
self.doneFunc(clearedAvIds)
|
||||||
|
return Task.done
|
||||||
|
|
||||||
|
def __handleUnexpectedExit(self, avId):
|
||||||
|
if avId not in self.avIdList:
|
||||||
|
return
|
||||||
|
self.avIdList.remove(avId)
|
||||||
|
if avId in self.pendingAvatars:
|
||||||
|
self.clear(avId)
|
156
otp/ai/MagicWordGlobal.py
Executable file
156
otp/ai/MagicWordGlobal.py
Executable file
|
@ -0,0 +1,156 @@
|
||||||
|
|
||||||
|
class MagicError(Exception): pass
|
||||||
|
|
||||||
|
def ensureAccess(access, msg='Insufficient access'):
|
||||||
|
if spellbook.getInvokerAccess() < access:
|
||||||
|
raise MagicError(msg)
|
||||||
|
|
||||||
|
class Spellbook:
|
||||||
|
"""
|
||||||
|
The Spellbook manages the list of all Magic Words that have been registered
|
||||||
|
anywhere in the system. When the MagicWordManager(AI) wants to process a
|
||||||
|
Magic Word, it is passed off to the Spellbook, which performs the operation.
|
||||||
|
|
||||||
|
To add Magic Words to the Spellbook, use the @magicWord() decorator.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.words = {}
|
||||||
|
|
||||||
|
self.currentInvoker = None
|
||||||
|
self.currentTarget = None
|
||||||
|
|
||||||
|
def addWord(self, word):
|
||||||
|
self.words[word.name.lower()] = word # lets make this stuff case insensitive
|
||||||
|
|
||||||
|
def process(self, invoker, target, incantation):
|
||||||
|
self.currentInvoker = invoker
|
||||||
|
self.currentTarget = target
|
||||||
|
word, args = (incantation.split(' ', 1) + [''])[:2]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.doWord(word, args)
|
||||||
|
except MagicError as e:
|
||||||
|
return e.message
|
||||||
|
except Exception:
|
||||||
|
return describeException(backTrace=1)
|
||||||
|
finally:
|
||||||
|
self.currentInvoker = None
|
||||||
|
self.currentTarget = None
|
||||||
|
|
||||||
|
def doWord(self, wordName, args):
|
||||||
|
word = self.words.get(wordName.lower()) # look it up by its lower case value
|
||||||
|
|
||||||
|
if not word:
|
||||||
|
if process == 'ai':
|
||||||
|
wname = wordName.lower()
|
||||||
|
for key in self.words:
|
||||||
|
if self.words.get(key).access <= self.getInvokerAccess():
|
||||||
|
if wname in key:
|
||||||
|
return 'Did you mean %s' % (self.words.get(key).name)
|
||||||
|
if not word:
|
||||||
|
return
|
||||||
|
|
||||||
|
ensureAccess(word.access)
|
||||||
|
if self.getTarget() and self.getTarget() != self.getInvoker():
|
||||||
|
if self.getInvokerAccess() <= self.getTarget().getAdminAccess():
|
||||||
|
raise MagicError('Target must have lower access')
|
||||||
|
|
||||||
|
result = word.run(args)
|
||||||
|
if result is not None:
|
||||||
|
return str(result)
|
||||||
|
|
||||||
|
def getInvoker(self):
|
||||||
|
return self.currentInvoker
|
||||||
|
|
||||||
|
def getTarget(self):
|
||||||
|
return self.currentTarget
|
||||||
|
|
||||||
|
def getInvokerAccess(self):
|
||||||
|
if not self.currentInvoker:
|
||||||
|
return 0
|
||||||
|
return self.currentInvoker.getAdminAccess()
|
||||||
|
|
||||||
|
spellbook = Spellbook()
|
||||||
|
|
||||||
|
|
||||||
|
# CATEGORIES
|
||||||
|
class MagicWordCategory:
|
||||||
|
def __init__(self, name, defaultAccess=600):
|
||||||
|
self.name = name
|
||||||
|
self.defaultAccess = defaultAccess
|
||||||
|
|
||||||
|
CATEGORY_UNKNOWN = MagicWordCategory('Unknown')
|
||||||
|
CATEGORY_USER = MagicWordCategory('Community manager', defaultAccess=100)
|
||||||
|
CATEGORY_COMMUNITY_MANAGER = MagicWordCategory('Community manager', defaultAccess=200)
|
||||||
|
CATEGORY_MODERATOR = MagicWordCategory('Moderator', defaultAccess=300)
|
||||||
|
CATEGORY_CREATIVE = MagicWordCategory('Creative', defaultAccess=400)
|
||||||
|
CATEGORY_PROGRAMMER = MagicWordCategory('Programmer', defaultAccess=500)
|
||||||
|
CATEGORY_ADMINISTRATOR = MagicWordCategory('Administrator', defaultAccess=600)
|
||||||
|
CATEGORY_SYSTEM_ADMINISTRATOR = MagicWordCategory('System administrator', defaultAccess=700)
|
||||||
|
|
||||||
|
MINIMUM_MAGICWORD_ACCESS = CATEGORY_COMMUNITY_MANAGER.defaultAccess
|
||||||
|
|
||||||
|
class MagicWord:
|
||||||
|
def __init__(self, name, func, types, access, doc):
|
||||||
|
self.name = name
|
||||||
|
self.func = func
|
||||||
|
self.types = types
|
||||||
|
self.access = access
|
||||||
|
self.doc = doc
|
||||||
|
|
||||||
|
def parseArgs(self, string):
|
||||||
|
maxArgs = self.func.func_code.co_argcount
|
||||||
|
minArgs = maxArgs - (len(self.func.func_defaults) if self.func.func_defaults else 0)
|
||||||
|
|
||||||
|
args = string.split(None, maxArgs-1)[:maxArgs]
|
||||||
|
if len(args) < minArgs:
|
||||||
|
raise MagicError('Magic word %s requires at least %d arguments' % (self.name, minArgs))
|
||||||
|
|
||||||
|
output = []
|
||||||
|
for i, (type, arg) in enumerate(zip(self.types, args)):
|
||||||
|
try:
|
||||||
|
targ = type(arg)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise MagicError('Argument %d of magic word %s must be %s' % (i, self.name, type.__name__))
|
||||||
|
|
||||||
|
output.append(targ)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def run(self, rawArgs):
|
||||||
|
args = self.parseArgs(rawArgs)
|
||||||
|
return self.func(*args)
|
||||||
|
|
||||||
|
|
||||||
|
class MagicWordDecorator:
|
||||||
|
"""
|
||||||
|
This class manages Magic Word decoration. It is aliased as magicWord, so that
|
||||||
|
the @magicWord(...) construct instantiates this class and has the resulting
|
||||||
|
object process the Magic Word's construction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name=None, types=[str], access=None, category=CATEGORY_UNKNOWN):
|
||||||
|
self.name = name
|
||||||
|
self.types = types
|
||||||
|
self.category = category
|
||||||
|
if access is not None:
|
||||||
|
self.access = access
|
||||||
|
else:
|
||||||
|
self.access = self.category.defaultAccess
|
||||||
|
|
||||||
|
def __call__(self, mw):
|
||||||
|
# This is the actual decoration routine. We add the function 'mw' as a
|
||||||
|
# Magic Word to the Spellbook, using the attributes specified at construction
|
||||||
|
# time.
|
||||||
|
|
||||||
|
name = self.name
|
||||||
|
if name is None:
|
||||||
|
name = mw.func_name
|
||||||
|
|
||||||
|
word = MagicWord(name, mw, self.types, self.access, mw.__doc__)
|
||||||
|
spellbook.addWord(word)
|
||||||
|
|
||||||
|
return mw
|
||||||
|
|
||||||
|
magicWord = MagicWordDecorator
|
43
otp/ai/MagicWordManager.py
Executable file
43
otp/ai/MagicWordManager.py
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed import DistributedObject
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
|
||||||
|
lastClickedNametag = None
|
||||||
|
|
||||||
|
class MagicWordManager(DistributedObject.DistributedObject):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('MagicWordManager')
|
||||||
|
neverDisable = 1
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedObject.DistributedObject.generate(self)
|
||||||
|
self.accept('magicWord', self.handleMagicWord)
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.ignore('magicWord')
|
||||||
|
DistributedObject.DistributedObject.disable(self)
|
||||||
|
|
||||||
|
def handleMagicWord(self, magicWord):
|
||||||
|
if not base.localAvatar.isAdmin():
|
||||||
|
return
|
||||||
|
|
||||||
|
if magicWord.startswith('~~'):
|
||||||
|
if lastClickedNametag == None:
|
||||||
|
target = base.localAvatar
|
||||||
|
else:
|
||||||
|
target = lastClickedNametag
|
||||||
|
magicWord = magicWord[2:]
|
||||||
|
if magicWord.startswith('~'):
|
||||||
|
target = base.localAvatar
|
||||||
|
magicWord = magicWord[1:]
|
||||||
|
|
||||||
|
targetId = target.doId
|
||||||
|
self.sendUpdate('sendMagicWord', [magicWord, targetId])
|
||||||
|
|
||||||
|
if target == base.localAvatar:
|
||||||
|
response = spellbook.process(base.localAvatar, target, magicWord)
|
||||||
|
if response:
|
||||||
|
self.sendMagicWordResponse(response)
|
||||||
|
|
||||||
|
def sendMagicWordResponse(self, response):
|
||||||
|
self.notify.info(response)
|
||||||
|
base.localAvatar.setSystemMessage(0, 'Spellbook: ' + str(response))
|
75
otp/ai/MagicWordManagerAI.py
Executable file
75
otp/ai/MagicWordManagerAI.py
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from direct.distributed.PyDatagram import PyDatagram
|
||||||
|
from direct.distributed.MsgTypes import *
|
||||||
|
|
||||||
|
class MagicWordManagerAI(DistributedObjectAI):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("MagicWordManagerAI")
|
||||||
|
|
||||||
|
def sendMagicWord(self, word, targetId):
|
||||||
|
invokerId = self.air.getAvatarIdFromSender()
|
||||||
|
invoker = self.air.doId2do.get(invokerId)
|
||||||
|
|
||||||
|
if not 'DistributedToonAI' in str(self.air.doId2do.get(targetId)):
|
||||||
|
self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['Target is not a toon object!'])
|
||||||
|
return
|
||||||
|
|
||||||
|
if not invoker:
|
||||||
|
self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['missing invoker'])
|
||||||
|
return
|
||||||
|
|
||||||
|
if not invoker.isAdmin():
|
||||||
|
self.air.writeServerEvent('suspicious', invokerId, 'Attempted to issue magic word: %s' % word)
|
||||||
|
dg = PyDatagram()
|
||||||
|
dg.addServerHeader(self.GetPuppetConnectionChannel(invokerId), self.air.ourChannel, CLIENTAGENT_EJECT)
|
||||||
|
dg.addUint16(102)
|
||||||
|
dg.addString('Magic Words are reserved for administrators only!')
|
||||||
|
self.air.send(dg)
|
||||||
|
return
|
||||||
|
|
||||||
|
target = self.air.doId2do.get(targetId)
|
||||||
|
if not target:
|
||||||
|
self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', ['missing target'])
|
||||||
|
return
|
||||||
|
|
||||||
|
response = spellbook.process(invoker, target, word)
|
||||||
|
if response:
|
||||||
|
self.sendUpdateToAvatarId(invokerId, 'sendMagicWordResponse', [response])
|
||||||
|
|
||||||
|
self.air.writeServerEvent('magic-word',
|
||||||
|
invokerId, invoker.getAdminAccess(),
|
||||||
|
targetId, target.getAdminAccess(),
|
||||||
|
word, response)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str])
|
||||||
|
def help(wordName=None):
|
||||||
|
if not wordName:
|
||||||
|
return "What were you interested getting help for?"
|
||||||
|
word = spellbook.words.get(wordName.lower())
|
||||||
|
if not word:
|
||||||
|
accessLevel = spellbook.getInvoker().getAdminAccess()
|
||||||
|
wname = wordName.lower()
|
||||||
|
for key in spellbook.words:
|
||||||
|
if spellbook.words.get(key).access <= accessLevel:
|
||||||
|
if wname in key:
|
||||||
|
return 'Did you mean %s' % (spellbook.words.get(key).name)
|
||||||
|
return 'I have no clue what %s is referring to' % (wordName)
|
||||||
|
return word.doc
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[])
|
||||||
|
def words():
|
||||||
|
accessLevel = spellbook.getInvoker().getAdminAccess()
|
||||||
|
wordString = None
|
||||||
|
for key in spellbook.words:
|
||||||
|
word = spellbook.words.get(key)
|
||||||
|
if word.access <= accessLevel:
|
||||||
|
if wordString is None:
|
||||||
|
wordString = key
|
||||||
|
else:
|
||||||
|
wordString += ", ";
|
||||||
|
wordString += key;
|
||||||
|
if wordString is None:
|
||||||
|
return "You are chopped liver"
|
||||||
|
else:
|
||||||
|
return wordString
|
127
otp/ai/TimeManager.py
Executable file
127
otp/ai/TimeManager.py
Executable file
|
@ -0,0 +1,127 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed import DistributedObject
|
||||||
|
from direct.distributed.ClockDelta import *
|
||||||
|
from direct.showbase import PythonUtil
|
||||||
|
from direct.showbase.DirectObject import *
|
||||||
|
from direct.task import Task
|
||||||
|
from panda3d.core import *
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
import time
|
||||||
|
|
||||||
|
class TimeManager(DistributedObject.DistributedObject):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('TimeManager')
|
||||||
|
neverDisable = 1
|
||||||
|
|
||||||
|
def __init__(self, cr):
|
||||||
|
DistributedObject.DistributedObject.__init__(self, cr)
|
||||||
|
self.updateFreq = base.config.GetFloat('time-manager-freq', 1800)
|
||||||
|
self.minWait = base.config.GetFloat('time-manager-min-wait', 10)
|
||||||
|
self.maxUncertainty = base.config.GetFloat('time-manager-max-uncertainty', 1)
|
||||||
|
self.maxAttempts = base.config.GetInt('time-manager-max-attempts', 5)
|
||||||
|
self.thisContext = -1
|
||||||
|
self.nextContext = 0
|
||||||
|
self.attemptCount = 0
|
||||||
|
self.start = 0
|
||||||
|
self.lastAttempt = -self.minWait * 2
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
self._gotFirstTimeSync = False
|
||||||
|
if self.cr.timeManager != None:
|
||||||
|
self.cr.timeManager.delete()
|
||||||
|
self.cr.timeManager = self
|
||||||
|
DistributedObject.DistributedObject.generate(self)
|
||||||
|
self.accept('clock_error', self.handleClockError)
|
||||||
|
if self.updateFreq > 0:
|
||||||
|
self.startTask()
|
||||||
|
self.setDisconnectReason(OTPGlobals.DisconnectNone)
|
||||||
|
return
|
||||||
|
|
||||||
|
def announceGenerate(self):
|
||||||
|
DistributedObject.DistributedObject.announceGenerate(self)
|
||||||
|
self.synchronize('TimeManager.announceGenerate')
|
||||||
|
|
||||||
|
def gotInitialTimeSync(self):
|
||||||
|
return self._gotFirstTimeSync
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.ignore('clock_error')
|
||||||
|
self.stopTask()
|
||||||
|
if self.cr.timeManager == self:
|
||||||
|
self.cr.timeManager = None
|
||||||
|
del self._gotFirstTimeSync
|
||||||
|
DistributedObject.DistributedObject.disable(self)
|
||||||
|
return
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.ignore('clock_error')
|
||||||
|
self.stopTask()
|
||||||
|
if self.cr.timeManager == self:
|
||||||
|
self.cr.timeManager = None
|
||||||
|
DistributedObject.DistributedObject.delete(self)
|
||||||
|
return
|
||||||
|
|
||||||
|
def startTask(self):
|
||||||
|
self.stopTask()
|
||||||
|
taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask')
|
||||||
|
|
||||||
|
def stopTask(self):
|
||||||
|
taskMgr.remove('timeMgrTask')
|
||||||
|
|
||||||
|
def doUpdate(self, task):
|
||||||
|
self.synchronize('timer')
|
||||||
|
taskMgr.doMethodLater(self.updateFreq, self.doUpdate, 'timeMgrTask')
|
||||||
|
return Task.done
|
||||||
|
|
||||||
|
def handleClockError(self):
|
||||||
|
self.synchronize('clock error')
|
||||||
|
|
||||||
|
def synchronize(self, description):
|
||||||
|
now = globalClock.getRealTime()
|
||||||
|
if now - self.lastAttempt < self.minWait:
|
||||||
|
self.notify.debug('Not resyncing (too soon): %s' % description)
|
||||||
|
return 0
|
||||||
|
self.thisContext = self.nextContext
|
||||||
|
self.attemptCount = 0
|
||||||
|
self.nextContext = self.nextContext + 1 & 255
|
||||||
|
self.notify.info('Clock sync: %s' % description)
|
||||||
|
self.start = now
|
||||||
|
self.lastAttempt = now
|
||||||
|
self.sendUpdate('requestServerTime', [self.thisContext])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def serverTime(self, context, timestamp, timeOfDay):
|
||||||
|
end = globalClock.getRealTime()
|
||||||
|
aiTimeSkew = timeOfDay - self.cr.getServerTimeOfDay()
|
||||||
|
if context != self.thisContext:
|
||||||
|
self.notify.info('Ignoring TimeManager response for old context %d' % context)
|
||||||
|
return
|
||||||
|
elapsed = end - self.start
|
||||||
|
self.attemptCount += 1
|
||||||
|
self.notify.info('Clock sync roundtrip took %0.3f ms' % (elapsed * 1000.0))
|
||||||
|
self.notify.info('AI time delta is %s from server delta' % PythonUtil.formatElapsedSeconds(aiTimeSkew))
|
||||||
|
average = (self.start + end) / 2.0
|
||||||
|
uncertainty = (end - self.start) / 2.0
|
||||||
|
globalClockDelta.resynchronize(average, timestamp, uncertainty)
|
||||||
|
self.notify.info('Local clock uncertainty +/- %.3f s' % globalClockDelta.getUncertainty())
|
||||||
|
if globalClockDelta.getUncertainty() > self.maxUncertainty:
|
||||||
|
if self.attemptCount < self.maxAttempts:
|
||||||
|
self.notify.info('Uncertainty is too high, trying again.')
|
||||||
|
self.start = globalClock.getRealTime()
|
||||||
|
self.sendUpdate('requestServerTime', [self.thisContext])
|
||||||
|
return
|
||||||
|
self.notify.info('Giving up on uncertainty requirement.')
|
||||||
|
self._gotFirstTimeSync = True
|
||||||
|
messenger.send('gotTimeSync')
|
||||||
|
|
||||||
|
toontownTimeManager = getattr(base.cr, 'toontownTimeManager', None)
|
||||||
|
if toontownTimeManager:
|
||||||
|
toontownTimeManager.updateLoginTimes(timeOfDay, int(time.time()), globalClock.getRealTime())
|
||||||
|
|
||||||
|
def setDisconnectReason(self, disconnectCode):
|
||||||
|
self.sendUpdate('setDisconnectReason', [disconnectCode])
|
||||||
|
|
||||||
|
def setExceptionInfo(self):
|
||||||
|
info = describeException()
|
||||||
|
self.notify.info('Client exception: %s' % info)
|
||||||
|
self.sendUpdate('setExceptionInfo', [info])
|
||||||
|
self.cr.flush()
|
30
otp/ai/TimeManagerAI.py
Executable file
30
otp/ai/TimeManagerAI.py
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
||||||
|
from direct.distributed.ClockDelta import globalClockDelta
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
import time
|
||||||
|
|
||||||
|
class TimeManagerAI(DistributedObjectAI):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("TimeManagerAI")
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
DistributedObjectAI.__init__(self, air)
|
||||||
|
self.avId2DcReason = {}
|
||||||
|
|
||||||
|
def requestServerTime(self, context):
|
||||||
|
self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'serverTime', [context, globalClockDelta.getRealNetworkTime(bits=32), int(time.time())])
|
||||||
|
|
||||||
|
def setDisconnectReason(self, reason):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
|
||||||
|
if reason == OTPGlobals.DisconnectNone and avId in self.avId2DcReason:
|
||||||
|
del self.avId2DcReason[avId]
|
||||||
|
else:
|
||||||
|
self.avId2DcReason[avId] = reason
|
||||||
|
|
||||||
|
def setExceptionInfo(self, exception):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
self.air.writeServerEvent('client-exception', avId, exception)
|
||||||
|
|
||||||
|
def getDisconnectReason(self, avId):
|
||||||
|
return self.avId2DcReason.get(avId, 0)
|
0
otp/ai/__init__.py
Executable file
0
otp/ai/__init__.py
Executable file
568
otp/avatar/Avatar.py
Executable file
568
otp/avatar/Avatar.py
Executable file
|
@ -0,0 +1,568 @@
|
||||||
|
from direct.actor.Actor import Actor
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed import ClockDelta
|
||||||
|
from direct.interval.IntervalGlobal import *
|
||||||
|
from direct.showbase.PythonUtil import recordCreationStack
|
||||||
|
from panda3d.core import *
|
||||||
|
import random
|
||||||
|
|
||||||
|
from otp.ai import MagicWordManager
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from otp.avatar.ShadowCaster import ShadowCaster
|
||||||
|
from otp.chat import ChatUtil
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
from otp.otpbase import OTPLocalizer
|
||||||
|
from otp.otpbase import OTPRender
|
||||||
|
from otp.nametag.Nametag import Nametag
|
||||||
|
from otp.nametag.NametagGroup import NametagGroup
|
||||||
|
from otp.nametag.NametagConstants import *
|
||||||
|
|
||||||
|
teleportNotify = DirectNotifyGlobal.directNotify.newCategory('Teleport')
|
||||||
|
teleportNotify.showTime = True
|
||||||
|
if config.GetBool('want-teleport-debug', 1):
|
||||||
|
teleportNotify.setDebug(1)
|
||||||
|
|
||||||
|
def reconsiderAllUnderstandable():
|
||||||
|
for av in Avatar.ActiveAvatars:
|
||||||
|
av.considerUnderstandable()
|
||||||
|
|
||||||
|
class Avatar(Actor, ShadowCaster):
|
||||||
|
notify = directNotify.newCategory('Avatar')
|
||||||
|
ActiveAvatars = []
|
||||||
|
|
||||||
|
def __init__(self, other = None):
|
||||||
|
Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
|
||||||
|
ShadowCaster.__init__(self)
|
||||||
|
self.__font = OTPGlobals.getInterfaceFont()
|
||||||
|
self.name = ''
|
||||||
|
self.soundChatBubble = None
|
||||||
|
self.avatarType = ''
|
||||||
|
self.nametagNodePath = None
|
||||||
|
self.__nameVisible = 1
|
||||||
|
self.nametag = NametagGroup()
|
||||||
|
self.nametag.setAvatar(self)
|
||||||
|
self.nametag.setFont(OTPGlobals.getInterfaceFont())
|
||||||
|
self.nametag.setSpeechFont(OTPGlobals.getInterfaceFont())
|
||||||
|
self.nametag2dContents = Nametag.CName | Nametag.CSpeech
|
||||||
|
self.nametag2dDist = Nametag.CName | Nametag.CSpeech
|
||||||
|
self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech
|
||||||
|
self.nametag3d = self.attachNewNode('nametag3d')
|
||||||
|
self.nametag3d.setTag('cam', 'nametag')
|
||||||
|
self.nametag3d.setLightOff()
|
||||||
|
self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
|
||||||
|
self.nametag3d.hide(OTPRender.ShadowCameraBitmask)
|
||||||
|
self.collTube = None
|
||||||
|
self.scale = 1.0
|
||||||
|
self.height = 0.0
|
||||||
|
self.style = None
|
||||||
|
self.understandable = 1
|
||||||
|
self.setPlayerType(NametagGroup.CCNormal)
|
||||||
|
self.ghostMode = 0
|
||||||
|
self.__chatParagraph = None
|
||||||
|
self.__chatMessage = None
|
||||||
|
self.__chatFlags = 0
|
||||||
|
self.__chatPageNumber = None
|
||||||
|
self.__chatAddressee = None
|
||||||
|
self.__chatDialogueList = []
|
||||||
|
self.__chatSet = 0
|
||||||
|
self.__chatLocal = 0
|
||||||
|
self.__currentDialogue = None
|
||||||
|
self.wantAdminTag = True
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
try:
|
||||||
|
self.Avatar_deleted
|
||||||
|
except:
|
||||||
|
self.deleteNametag3d()
|
||||||
|
Actor.cleanup(self)
|
||||||
|
self.Avatar_deleted = 1
|
||||||
|
del self.__font
|
||||||
|
del self.style
|
||||||
|
del self.soundChatBubble
|
||||||
|
self.nametag.destroy()
|
||||||
|
del self.nametag
|
||||||
|
self.nametag3d.removeNode()
|
||||||
|
ShadowCaster.delete(self)
|
||||||
|
Actor.delete(self)
|
||||||
|
|
||||||
|
def isLocal(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def isPet(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isProxy(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def setPlayerType(self, playerType):
|
||||||
|
self.playerType = playerType
|
||||||
|
if not hasattr(self, 'nametag'):
|
||||||
|
self.notify.warning('no nametag attributed, but would have been used.')
|
||||||
|
return
|
||||||
|
if self.isUnderstandable():
|
||||||
|
self.nametag.setColorCode(self.playerType)
|
||||||
|
else:
|
||||||
|
self.nametag.setColorCode(NametagGroup.CCNonPlayer)
|
||||||
|
self.setNametagName()
|
||||||
|
|
||||||
|
def considerUnderstandable(self):
|
||||||
|
if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCSpeedChat):
|
||||||
|
self.setPlayerType(NametagGroup.CCSpeedChat)
|
||||||
|
if hasattr(base, 'localAvatar') and (self == base.localAvatar):
|
||||||
|
self.understandable = 1
|
||||||
|
self.setPlayerType(NametagGroup.CCNormal)
|
||||||
|
elif self.playerType == NametagGroup.CCSuit:
|
||||||
|
self.understandable = 1
|
||||||
|
self.setPlayerType(NametagGroup.CCSuit)
|
||||||
|
elif self.playerType not in (NametagGroup.CCNormal, NametagGroup.CCSpeedChat):
|
||||||
|
self.understandable = 1
|
||||||
|
self.setPlayerType(NametagGroup.CCNonPlayer)
|
||||||
|
elif base.localAvatar.isTrueFriends(self.doId):
|
||||||
|
self.understandable = 2
|
||||||
|
self.setPlayerType(NametagGroup.CCNormal)
|
||||||
|
elif base.cr.wantSpeedchatPlus():
|
||||||
|
self.understandable = 1
|
||||||
|
self.setPlayerType(NametagGroup.CCSpeedChat)
|
||||||
|
else:
|
||||||
|
self.understandable = 0
|
||||||
|
self.setPlayerType(NametagGroup.CCSpeedChat)
|
||||||
|
if base.cr.wantSpeedchatPlus() and hasattr(self, 'adminAccess') and self.isAdmin() and self != base.localAvatar:
|
||||||
|
self.understandable = 2
|
||||||
|
if not hasattr(self, 'nametag'):
|
||||||
|
self.notify.warning('no nametag attributed, but would have been used')
|
||||||
|
else:
|
||||||
|
self.nametag.setColorCode(self.playerType)
|
||||||
|
|
||||||
|
def isUnderstandable(self):
|
||||||
|
return self.understandable
|
||||||
|
|
||||||
|
def setDNAString(self, dnaString):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setDNA(self, dna):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getAvatarScale(self):
|
||||||
|
return self.scale
|
||||||
|
|
||||||
|
def setAvatarScale(self, scale):
|
||||||
|
if self.scale != scale:
|
||||||
|
self.scale = scale
|
||||||
|
self.getGeomNode().setScale(scale)
|
||||||
|
self.setHeight(self.height)
|
||||||
|
|
||||||
|
def adjustNametag3d(self, parentScale = 1.0):
|
||||||
|
self.nametag3d.setPos(0, 0, self.height + 0.5)
|
||||||
|
|
||||||
|
def getHeight(self):
|
||||||
|
return self.height
|
||||||
|
|
||||||
|
def setHeight(self, height):
|
||||||
|
self.height = height
|
||||||
|
self.adjustNametag3d()
|
||||||
|
if self.collTube:
|
||||||
|
self.collTube.setPointB(0, 0, height - self.getRadius())
|
||||||
|
if self.collNodePath:
|
||||||
|
self.collNodePath.forceRecomputeBounds()
|
||||||
|
|
||||||
|
def getRadius(self):
|
||||||
|
return OTPGlobals.AvatarDefaultRadius
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def getType(self):
|
||||||
|
return self.avatarType
|
||||||
|
|
||||||
|
def setWantAdminTag(self, bool):
|
||||||
|
self.wantAdminTag = bool
|
||||||
|
|
||||||
|
def getWantAdminTag(self):
|
||||||
|
return self.wantAdminTag
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
if hasattr(self, 'isDisguised') and self.isDisguised:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if hasattr(self, 'nametag'):
|
||||||
|
self.setNametagName()
|
||||||
|
|
||||||
|
def setDisplayName(self, str):
|
||||||
|
if hasattr(self, 'isDisguised'):
|
||||||
|
if self.isDisguised:
|
||||||
|
return
|
||||||
|
self.setNametagName(str)
|
||||||
|
|
||||||
|
def setNametagName(self, name=None):
|
||||||
|
if not name:
|
||||||
|
name = self.name
|
||||||
|
|
||||||
|
self.nametag.setName(name)
|
||||||
|
|
||||||
|
if hasattr(self, 'adminAccess') and self.isAdmin() and self.getWantAdminTag():
|
||||||
|
access = self.getAdminAccess()
|
||||||
|
|
||||||
|
if access in OTPLocalizer.AccessToString:
|
||||||
|
name += '\n\x01shadow\x01%s\x02' % OTPLocalizer.AccessToString[access]
|
||||||
|
|
||||||
|
self.nametag.setDisplayName(name)
|
||||||
|
|
||||||
|
def getFont(self):
|
||||||
|
return self.__font
|
||||||
|
|
||||||
|
def setFont(self, font):
|
||||||
|
self.__font = font
|
||||||
|
self.nametag.setFont(font)
|
||||||
|
|
||||||
|
def getStyle(self):
|
||||||
|
return self.style
|
||||||
|
|
||||||
|
def setStyle(self, style):
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
def getDialogueArray(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1):
|
||||||
|
if interrupt and self.__currentDialogue is not None:
|
||||||
|
self.__currentDialogue.stop()
|
||||||
|
self.__currentDialogue = dialogue
|
||||||
|
if dialogue:
|
||||||
|
base.playSfx(dialogue, node=self)
|
||||||
|
elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0:
|
||||||
|
self.playDialogueForString(self.nametag.getChat())
|
||||||
|
if self.soundChatBubble != None:
|
||||||
|
base.playSfx(self.soundChatBubble, node=self)
|
||||||
|
|
||||||
|
def playDialogueForString(self, chatString):
|
||||||
|
searchString = chatString.lower()
|
||||||
|
if searchString.find(OTPLocalizer.DialogSpecial) >= 0:
|
||||||
|
type = 'special'
|
||||||
|
elif searchString.find(OTPLocalizer.DialogExclamation) >= 0:
|
||||||
|
type = 'exclamation'
|
||||||
|
elif searchString.find(OTPLocalizer.DialogQuestion) >= 0:
|
||||||
|
type = 'question'
|
||||||
|
elif random.randint(0, 1):
|
||||||
|
type = 'statementA'
|
||||||
|
else:
|
||||||
|
type = 'statementB'
|
||||||
|
stringLength = len(chatString)
|
||||||
|
if stringLength <= OTPLocalizer.DialogLength1:
|
||||||
|
length = 1
|
||||||
|
elif stringLength <= OTPLocalizer.DialogLength2:
|
||||||
|
length = 2
|
||||||
|
elif stringLength <= OTPLocalizer.DialogLength3:
|
||||||
|
length = 3
|
||||||
|
else:
|
||||||
|
length = 4
|
||||||
|
self.playDialogue(type, length)
|
||||||
|
|
||||||
|
def playDialogue(self, type, length):
|
||||||
|
dialogueArray = self.getDialogueArray()
|
||||||
|
if dialogueArray == None:
|
||||||
|
return
|
||||||
|
sfxIndex = None
|
||||||
|
if type == 'statementA' or type == 'statementB':
|
||||||
|
if length == 1:
|
||||||
|
sfxIndex = 0
|
||||||
|
elif length == 2:
|
||||||
|
sfxIndex = 1
|
||||||
|
elif length >= 3:
|
||||||
|
sfxIndex = 2
|
||||||
|
elif type == 'question':
|
||||||
|
sfxIndex = 3
|
||||||
|
elif type == 'exclamation':
|
||||||
|
sfxIndex = 4
|
||||||
|
elif type == 'special':
|
||||||
|
sfxIndex = 5
|
||||||
|
else:
|
||||||
|
notify.error('unrecognized dialogue type: ', type)
|
||||||
|
if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
|
||||||
|
base.playSfx(dialogueArray[sfxIndex], node=self)
|
||||||
|
return
|
||||||
|
|
||||||
|
def getDialogueSfx(self, type, length):
|
||||||
|
retval = None
|
||||||
|
dialogueArray = self.getDialogueArray()
|
||||||
|
if dialogueArray == None:
|
||||||
|
return
|
||||||
|
sfxIndex = None
|
||||||
|
if type == 'statementA' or type == 'statementB':
|
||||||
|
if length == 1:
|
||||||
|
sfxIndex = 0
|
||||||
|
elif length == 2:
|
||||||
|
sfxIndex = 1
|
||||||
|
elif length >= 3:
|
||||||
|
sfxIndex = 2
|
||||||
|
elif type == 'question':
|
||||||
|
sfxIndex = 3
|
||||||
|
elif type == 'exclamation':
|
||||||
|
sfxIndex = 4
|
||||||
|
elif type == 'special':
|
||||||
|
sfxIndex = 5
|
||||||
|
else:
|
||||||
|
notify.error('unrecognized dialogue type: ', type)
|
||||||
|
if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
|
||||||
|
retval = dialogueArray[sfxIndex]
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def setChatAbsolute(self, chatString, chatFlags, dialogue=None, interrupt=1):
|
||||||
|
self.clearChat()
|
||||||
|
self.nametag.setChat(chatString, chatFlags)
|
||||||
|
self.playCurrentDialogue(dialogue, chatFlags, interrupt)
|
||||||
|
|
||||||
|
def displayTalk(self, chatString):
|
||||||
|
if not base.localAvatar.isIgnored(self.doId):
|
||||||
|
self.clearChat()
|
||||||
|
if ChatUtil.isThought(chatString):
|
||||||
|
chatString = ChatUtil.removeThoughtPrefix(chatString)
|
||||||
|
self.nametag.setChat(chatString, CFThought)
|
||||||
|
else:
|
||||||
|
self.nametag.setChat(chatString, CFSpeech | CFTimeout)
|
||||||
|
|
||||||
|
def clearChat(self):
|
||||||
|
self.nametag.clearChat()
|
||||||
|
|
||||||
|
def getNameVisible(self):
|
||||||
|
return self.__nameVisible
|
||||||
|
|
||||||
|
def setNameVisible(self, bool):
|
||||||
|
self.__nameVisible = bool
|
||||||
|
if bool:
|
||||||
|
self.showName()
|
||||||
|
if not bool:
|
||||||
|
self.hideName()
|
||||||
|
|
||||||
|
def hideName(self):
|
||||||
|
nametag3d = self.nametag.getNametag3d()
|
||||||
|
nametag3d.setContents(Nametag.CSpeech | Nametag.CThought)
|
||||||
|
|
||||||
|
def showName(self):
|
||||||
|
if self.__nameVisible and (not self.ghostMode):
|
||||||
|
nametag3d = self.nametag.getNametag3d()
|
||||||
|
nametag3d.setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
|
||||||
|
|
||||||
|
def hideNametag2d(self):
|
||||||
|
nametag2d = self.nametag.getNametag2d()
|
||||||
|
self.nametag2dContents = 0
|
||||||
|
nametag2d.setContents(self.nametag2dContents & self.nametag2dDist)
|
||||||
|
|
||||||
|
def showNametag2d(self):
|
||||||
|
nametag2d = self.nametag.getNametag2d()
|
||||||
|
self.nametag2dContents = self.nametag2dNormalContents
|
||||||
|
if self.ghostMode:
|
||||||
|
self.nametag2dContents = Nametag.CSpeech
|
||||||
|
nametag2d.setContents(self.nametag2dContents & self.nametag2dDist)
|
||||||
|
|
||||||
|
def hideNametag3d(self):
|
||||||
|
nametag3d = self.nametag.getNametag3d()
|
||||||
|
nametag3d.setContents(0)
|
||||||
|
|
||||||
|
def showNametag3d(self):
|
||||||
|
nametag3d = self.nametag.getNametag3d()
|
||||||
|
if self.__nameVisible and (not self.ghostMode):
|
||||||
|
nametag3d.setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
|
||||||
|
else:
|
||||||
|
nametag3d.setContents(0)
|
||||||
|
|
||||||
|
def setPickable(self, flag):
|
||||||
|
self.nametag.setActive(flag)
|
||||||
|
|
||||||
|
def clickedNametag(self):
|
||||||
|
MagicWordManager.lastClickedNametag = self
|
||||||
|
if self.nametag.hasButton():
|
||||||
|
self.advancePageNumber()
|
||||||
|
elif self.nametag.isActive():
|
||||||
|
messenger.send('clickedNametag', [self])
|
||||||
|
|
||||||
|
def setPageChat(self, addressee, paragraph, message, quitButton,
|
||||||
|
extraChatFlags=None, dialogueList=[], pageButton=True):
|
||||||
|
self.__chatAddressee = addressee
|
||||||
|
self.__chatPageNumber = None
|
||||||
|
self.__chatParagraph = paragraph
|
||||||
|
self.__chatMessage = message
|
||||||
|
if extraChatFlags is None:
|
||||||
|
self.__chatFlags = CFSpeech
|
||||||
|
else:
|
||||||
|
self.__chatFlags = CFSpeech | extraChatFlags
|
||||||
|
self.__chatDialogueList = dialogueList
|
||||||
|
self.__chatSet = 0
|
||||||
|
self.__chatLocal = 0
|
||||||
|
self.__updatePageChat()
|
||||||
|
if addressee == base.localAvatar.doId:
|
||||||
|
if pageButton:
|
||||||
|
self.__chatFlags |= CFPageButton
|
||||||
|
if quitButton == None:
|
||||||
|
self.__chatFlags |= CFNoQuitButton
|
||||||
|
elif quitButton:
|
||||||
|
self.__chatFlags |= CFQuitButton
|
||||||
|
self.b_setPageNumber(self.__chatParagraph, 0)
|
||||||
|
|
||||||
|
def setLocalPageChat(self, message, quitButton, extraChatFlags=None,
|
||||||
|
dialogueList=[]):
|
||||||
|
self.__chatAddressee = base.localAvatar.doId
|
||||||
|
self.__chatPageNumber = None
|
||||||
|
self.__chatParagraph = None
|
||||||
|
self.__chatMessage = message
|
||||||
|
if extraChatFlags is None:
|
||||||
|
self.__chatFlags = CFSpeech
|
||||||
|
else:
|
||||||
|
self.__chatFlags = CFSpeech | extraChatFlags
|
||||||
|
self.__chatDialogueList = dialogueList
|
||||||
|
self.__chatSet = 1
|
||||||
|
self.__chatLocal = 1
|
||||||
|
self.__chatFlags |= CFPageButton
|
||||||
|
if quitButton == None:
|
||||||
|
self.__chatFlags |= CFNoQuitButton
|
||||||
|
elif quitButton:
|
||||||
|
self.__chatFlags |= CFQuitButton
|
||||||
|
if len(dialogueList) > 0:
|
||||||
|
dialogue = dialogueList[0]
|
||||||
|
else:
|
||||||
|
dialogue = None
|
||||||
|
self.clearChat()
|
||||||
|
self.setChatAbsolute(message, self.__chatFlags, dialogue)
|
||||||
|
self.setPageNumber(None, 0)
|
||||||
|
|
||||||
|
def setPageNumber(self, paragraph, pageNumber, timestamp=None):
|
||||||
|
if timestamp is None:
|
||||||
|
elapsed = 0.0
|
||||||
|
else:
|
||||||
|
elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
|
||||||
|
self.__chatPageNumber = [paragraph, pageNumber]
|
||||||
|
self.__updatePageChat()
|
||||||
|
if hasattr(self, 'uniqueName'):
|
||||||
|
if pageNumber >= 0:
|
||||||
|
messenger.send(self.uniqueName('nextChatPage'), [pageNumber, elapsed])
|
||||||
|
else:
|
||||||
|
messenger.send(self.uniqueName('doneChatPage'), [elapsed])
|
||||||
|
elif pageNumber >= 0:
|
||||||
|
messenger.send('nextChatPage', [pageNumber, elapsed])
|
||||||
|
else:
|
||||||
|
messenger.send('doneChatPage', [elapsed])
|
||||||
|
|
||||||
|
def advancePageNumber(self):
|
||||||
|
if (self.__chatAddressee == base.localAvatar.doId) and (
|
||||||
|
self.__chatPageNumber is not None) and (
|
||||||
|
self.__chatPageNumber[0] == self.__chatParagraph):
|
||||||
|
pageNumber = self.__chatPageNumber[1]
|
||||||
|
if pageNumber >= 0:
|
||||||
|
pageNumber += 1
|
||||||
|
if pageNumber >= self.nametag.getNumChatPages():
|
||||||
|
pageNumber = -1
|
||||||
|
if self.__chatLocal:
|
||||||
|
self.setPageNumber(self.__chatParagraph, pageNumber)
|
||||||
|
else:
|
||||||
|
self.b_setPageNumber(self.__chatParagraph, pageNumber)
|
||||||
|
|
||||||
|
def __updatePageChat(self):
|
||||||
|
if (self.__chatPageNumber is not None) and (
|
||||||
|
self.__chatPageNumber[0] == self.__chatParagraph):
|
||||||
|
pageNumber = self.__chatPageNumber[1]
|
||||||
|
if pageNumber >= 0:
|
||||||
|
if not self.__chatSet:
|
||||||
|
if len(self.__chatDialogueList) > 0:
|
||||||
|
dialogue = self.__chatDialogueList[0]
|
||||||
|
else:
|
||||||
|
dialogue = None
|
||||||
|
self.setChatAbsolute(self.__chatMessage, self.__chatFlags, dialogue)
|
||||||
|
self.__chatSet = 1
|
||||||
|
if pageNumber < self.nametag.getNumChatPages():
|
||||||
|
self.nametag.setPageNumber(pageNumber)
|
||||||
|
if pageNumber > 0:
|
||||||
|
if len(self.__chatDialogueList) > pageNumber:
|
||||||
|
dialogue = self.__chatDialogueList[pageNumber]
|
||||||
|
else:
|
||||||
|
dialogue = None
|
||||||
|
self.playCurrentDialogue(dialogue, self.__chatFlags)
|
||||||
|
else:
|
||||||
|
self.clearChat()
|
||||||
|
else:
|
||||||
|
self.clearChat()
|
||||||
|
|
||||||
|
def getAirborneHeight(self):
|
||||||
|
height = self.getPos(self.shadowPlacer.shadowNodePath)
|
||||||
|
return height.getZ() + 0.025
|
||||||
|
|
||||||
|
def initializeNametag3d(self):
|
||||||
|
self.deleteNametag3d()
|
||||||
|
nametagNode = self.nametag.getNametag3d()
|
||||||
|
self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
|
||||||
|
iconNodePath = self.nametag.getNameIcon()
|
||||||
|
for cJoint in self.getNametagJoints():
|
||||||
|
cJoint.clearNetTransforms()
|
||||||
|
cJoint.addNetTransform(nametagNode)
|
||||||
|
|
||||||
|
def deleteNametag3d(self):
|
||||||
|
if self.nametagNodePath:
|
||||||
|
self.nametagNodePath.removeNode()
|
||||||
|
self.nametagNodePath = None
|
||||||
|
|
||||||
|
def initializeBodyCollisions(self, collIdStr):
|
||||||
|
self.collTube = CollisionTube(0, 0, 0.5, 0, 0, self.height - self.getRadius(), self.getRadius())
|
||||||
|
self.collNode = CollisionNode(collIdStr)
|
||||||
|
self.collNode.addSolid(self.collTube)
|
||||||
|
self.collNodePath = self.attachNewNode(self.collNode)
|
||||||
|
if self.ghostMode:
|
||||||
|
self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
|
||||||
|
else:
|
||||||
|
self.collNode.setCollideMask(OTPGlobals.WallBitmask)
|
||||||
|
|
||||||
|
def stashBodyCollisions(self):
|
||||||
|
if hasattr(self, 'collNodePath'):
|
||||||
|
self.collNodePath.stash()
|
||||||
|
|
||||||
|
def unstashBodyCollisions(self):
|
||||||
|
if hasattr(self, 'collNodePath'):
|
||||||
|
self.collNodePath.unstash()
|
||||||
|
|
||||||
|
def disableBodyCollisions(self):
|
||||||
|
if hasattr(self, 'collNodePath'):
|
||||||
|
self.collNodePath.removeNode()
|
||||||
|
del self.collNodePath
|
||||||
|
self.collTube = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def addActive(self):
|
||||||
|
if base.wantNametags:
|
||||||
|
try:
|
||||||
|
Avatar.ActiveAvatars.remove(self)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
Avatar.ActiveAvatars.append(self)
|
||||||
|
self.nametag.manage(base.marginManager)
|
||||||
|
self.accept(self.nametag.getUniqueId(), self.clickedNametag)
|
||||||
|
|
||||||
|
def removeActive(self):
|
||||||
|
if base.wantNametags:
|
||||||
|
try:
|
||||||
|
Avatar.ActiveAvatars.remove(self)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.nametag.unmanage(base.marginManager)
|
||||||
|
self.ignore(self.nametag.getUniqueId())
|
||||||
|
|
||||||
|
def loop(self, animName, restart = 1, partName = None, fromFrame = None, toFrame = None):
|
||||||
|
return Actor.loop(self, animName, restart, partName, fromFrame, toFrame)
|
||||||
|
|
||||||
|
def createTalkSequence(self, speech, waitTime, name='talkSequence'):
|
||||||
|
sequence = Sequence(name=name)
|
||||||
|
|
||||||
|
for text in speech:
|
||||||
|
sequence.append(Func(self.setChatAbsolute, text, CFSpeech))
|
||||||
|
sequence.append(Wait(len(text.split(' '))))
|
||||||
|
sequence.append(Func(self.clearChat))
|
||||||
|
sequence.append(Wait(waitTime))
|
||||||
|
|
||||||
|
return sequence
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER)
|
||||||
|
def target():
|
||||||
|
"""
|
||||||
|
Returns the current Spellbook target.
|
||||||
|
"""
|
||||||
|
return 'Your current target is: %s [avId: %s, access: %s]' % (spellbook.getTarget().getName(), spellbook.getTarget().doId, spellbook.getTarget().getAdminAccess())
|
51
otp/avatar/AvatarDetail.py
Executable file
51
otp/avatar/AvatarDetail.py
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
from direct.directnotify.DirectNotifyGlobal import directNotify
|
||||||
|
from otp.avatar import Avatar
|
||||||
|
|
||||||
|
class AvatarDetail:
|
||||||
|
notify = directNotify.newCategory('AvatarDetail')
|
||||||
|
|
||||||
|
def __init__(self, doId, callWhenDone):
|
||||||
|
self.id = doId
|
||||||
|
self.callWhenDone = callWhenDone
|
||||||
|
self.enterQuery()
|
||||||
|
|
||||||
|
def isReady(self):
|
||||||
|
return true
|
||||||
|
|
||||||
|
def getId(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def enterQuery(self):
|
||||||
|
self.avatar = base.cr.doId2do.get(self.id)
|
||||||
|
if self.avatar != None and not self.avatar.ghostMode:
|
||||||
|
self.createdAvatar = 0
|
||||||
|
dclass = self.getDClass()
|
||||||
|
self.__handleResponse(True, self.avatar, dclass)
|
||||||
|
else:
|
||||||
|
self.avatar = self.createHolder()
|
||||||
|
self.createdAvatar = 1
|
||||||
|
self.avatar.doId = self.id
|
||||||
|
dclass = self.getDClass()
|
||||||
|
base.cr.getAvatarDetails(self.avatar, self.__handleResponse, dclass)
|
||||||
|
return
|
||||||
|
|
||||||
|
def exitQuery(self):
|
||||||
|
return true
|
||||||
|
|
||||||
|
def createHolder(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getDClass(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __handleResponse(self, gotData, avatar, dclass):
|
||||||
|
if avatar != self.avatar:
|
||||||
|
self.notify.warning('Ignoring unexpected request for avatar %s' % avatar.doId)
|
||||||
|
return
|
||||||
|
if gotData:
|
||||||
|
self.callWhenDone(self.avatar)
|
||||||
|
del self.callWhenDone
|
||||||
|
else:
|
||||||
|
self.callWhenDone(None)
|
||||||
|
del self.callWhenDone
|
||||||
|
return
|
75
otp/avatar/AvatarPanel.py
Executable file
75
otp/avatar/AvatarPanel.py
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
from panda3d.core import *
|
||||||
|
from direct.gui.DirectGui import *
|
||||||
|
from direct.showbase import DirectObject
|
||||||
|
import Avatar
|
||||||
|
from direct.distributed import DistributedObject
|
||||||
|
|
||||||
|
class AvatarPanel(DirectObject.DirectObject):
|
||||||
|
currentAvatarPanel = None
|
||||||
|
|
||||||
|
def __init__(self, avatar, FriendsListPanel = None):
|
||||||
|
if AvatarPanel.currentAvatarPanel:
|
||||||
|
AvatarPanel.currentAvatarPanel.cleanup()
|
||||||
|
AvatarPanel.currentAvatarPanel = self
|
||||||
|
self.friendsListShown = False
|
||||||
|
self.FriendsListPanel = FriendsListPanel
|
||||||
|
if FriendsListPanel:
|
||||||
|
self.friendsListShown = FriendsListPanel.isFriendsListShown()
|
||||||
|
FriendsListPanel.hideFriendsList()
|
||||||
|
if avatar:
|
||||||
|
self.avatar = avatar
|
||||||
|
self.avName = avatar.getName()
|
||||||
|
else:
|
||||||
|
self.avatar = None
|
||||||
|
self.avName = 'Avatar'
|
||||||
|
if hasattr(avatar, 'uniqueName'):
|
||||||
|
self.avId = avatar.doId
|
||||||
|
self.avDisableName = avatar.uniqueName('disable')
|
||||||
|
self.avGenerateName = avatar.uniqueName('generate')
|
||||||
|
self.avHpChangeName = avatar.uniqueName('hpChange')
|
||||||
|
if self.avId in base.cr.doId2do:
|
||||||
|
self.avatar = base.cr.doId2do[self.avId]
|
||||||
|
else:
|
||||||
|
self.avDisableName = None
|
||||||
|
self.avGenerateName = None
|
||||||
|
self.avHpChangeName = None
|
||||||
|
self.avId = None
|
||||||
|
if self.avDisableName:
|
||||||
|
self.accept(self.avDisableName, self.__handleDisableAvatar)
|
||||||
|
return
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
if AvatarPanel.currentAvatarPanel != self:
|
||||||
|
return
|
||||||
|
if self.avDisableName:
|
||||||
|
self.ignore(self.avDisableName)
|
||||||
|
if self.avGenerateName:
|
||||||
|
self.ignore(self.avGenerateName)
|
||||||
|
if self.avHpChangeName:
|
||||||
|
self.ignore(self.avHpChangeName)
|
||||||
|
AvatarPanel.currentAvatarPanel = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def __handleClose(self):
|
||||||
|
self.cleanup()
|
||||||
|
AvatarPanel.currentAvatarPanel = None
|
||||||
|
if self.friendsListShown:
|
||||||
|
self.FriendsListPanel.showFriendsList()
|
||||||
|
return
|
||||||
|
|
||||||
|
def __handleDisableAvatar(self):
|
||||||
|
if AvatarPanel.currentAvatarPanel:
|
||||||
|
AvatarPanel.currentAvatarPanel.handleDisableAvatar()
|
||||||
|
else:
|
||||||
|
self.handleDisableAvatar()
|
||||||
|
|
||||||
|
def handleDisableAvatar(self):
|
||||||
|
self.cleanup()
|
||||||
|
AvatarPanel.currentAvatarPanel = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def isHidden(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def getType(self):
|
||||||
|
return None
|
346
otp/avatar/DistributedAvatar.py
Executable file
346
otp/avatar/DistributedAvatar.py
Executable file
|
@ -0,0 +1,346 @@
|
||||||
|
from direct.actor.DistributedActor import DistributedActor
|
||||||
|
from direct.distributed import DistributedNode
|
||||||
|
from direct.interval.IntervalGlobal import *
|
||||||
|
from direct.showbase import PythonUtil
|
||||||
|
from direct.task import Task
|
||||||
|
from panda3d.core import *
|
||||||
|
|
||||||
|
from Avatar import Avatar
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
from toontown.battle.BattleProps import globalPropPool
|
||||||
|
from otp.nametag.Nametag import Nametag
|
||||||
|
|
||||||
|
|
||||||
|
class DistributedAvatar(DistributedActor, Avatar):
|
||||||
|
HpTextGenerator = TextNode('HpTextGenerator')
|
||||||
|
HpTextEnabled = 1
|
||||||
|
ManagesNametagAmbientLightChanged = True
|
||||||
|
|
||||||
|
def __init__(self, cr):
|
||||||
|
try:
|
||||||
|
self.DistributedAvatar_initialized
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
self.DistributedAvatar_initialized = 1
|
||||||
|
|
||||||
|
Avatar.__init__(self)
|
||||||
|
DistributedActor.__init__(self, cr)
|
||||||
|
self.hpText = None
|
||||||
|
self.hp = None
|
||||||
|
self.maxHp = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
try:
|
||||||
|
del self.DistributedAvatar_announced
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.reparentTo(hidden)
|
||||||
|
self.removeActive()
|
||||||
|
self.disableBodyCollisions()
|
||||||
|
self.hideHpText()
|
||||||
|
self.hp = None
|
||||||
|
self.ignore('nameTagShowAvId')
|
||||||
|
self.ignore('nameTagShowName')
|
||||||
|
DistributedActor.disable(self)
|
||||||
|
return
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
try:
|
||||||
|
self.DistributedAvatar_deleted
|
||||||
|
except:
|
||||||
|
self.DistributedAvatar_deleted = 1
|
||||||
|
Avatar.delete(self)
|
||||||
|
DistributedActor.delete(self)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedActor.generate(self)
|
||||||
|
if not self.isLocal():
|
||||||
|
self.addActive()
|
||||||
|
self.considerUnderstandable()
|
||||||
|
self.setParent(OTPGlobals.SPHidden)
|
||||||
|
self.setTag('avatarDoId', str(self.doId))
|
||||||
|
self.accept('nameTagShowAvId', self.__nameTagShowAvId)
|
||||||
|
self.accept('nameTagShowName', self.__nameTagShowName)
|
||||||
|
|
||||||
|
def announceGenerate(self):
|
||||||
|
try:
|
||||||
|
self.DistributedAvatar_announced
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
self.DistributedAvatar_announced = 1
|
||||||
|
|
||||||
|
if not self.isLocal():
|
||||||
|
self.initializeBodyCollisions('distAvatarCollNode-' + str(self.doId))
|
||||||
|
DistributedActor.announceGenerate(self)
|
||||||
|
|
||||||
|
def __setTags(self, extra = None):
|
||||||
|
if hasattr(base, 'idTags'):
|
||||||
|
if base.idTags:
|
||||||
|
self.__nameTagShowAvId()
|
||||||
|
else:
|
||||||
|
self.__nameTagShowName()
|
||||||
|
|
||||||
|
def do_setParent(self, parentToken):
|
||||||
|
if not self.isDisabled():
|
||||||
|
if parentToken == OTPGlobals.SPHidden:
|
||||||
|
self.nametag2dDist &= ~Nametag.CName
|
||||||
|
else:
|
||||||
|
self.nametag2dDist |= Nametag.CName
|
||||||
|
self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
|
||||||
|
DistributedActor.do_setParent(self, parentToken)
|
||||||
|
self.__setTags()
|
||||||
|
|
||||||
|
def toonUp(self, hpGained):
|
||||||
|
if self.hp == None or hpGained < 0:
|
||||||
|
return
|
||||||
|
oldHp = self.hp
|
||||||
|
if self.hp + hpGained <= 0:
|
||||||
|
self.hp += hpGained
|
||||||
|
else:
|
||||||
|
self.hp = min(max(self.hp, 0) + hpGained, self.maxHp)
|
||||||
|
hpGained = self.hp - max(oldHp, 0)
|
||||||
|
if hpGained > 0:
|
||||||
|
self.showHpText(hpGained)
|
||||||
|
self.hpChange(quietly=0)
|
||||||
|
return
|
||||||
|
|
||||||
|
def takeDamage(self, hpLost, bonus = 0):
|
||||||
|
if self.hp == None or hpLost < 0:
|
||||||
|
return
|
||||||
|
oldHp = self.hp
|
||||||
|
self.hp = max(self.hp - hpLost, 0)
|
||||||
|
hpLost = oldHp - self.hp
|
||||||
|
if hpLost > 0:
|
||||||
|
self.showHpText(-hpLost, bonus)
|
||||||
|
self.hpChange(quietly=0)
|
||||||
|
if self.hp <= 0 and oldHp > 0:
|
||||||
|
self.died()
|
||||||
|
return
|
||||||
|
|
||||||
|
def setHp(self, hitPoints):
|
||||||
|
justRanOutOfHp = (hitPoints is not None and self.hp is not None and self.hp - hitPoints > 0) and (hitPoints <= 0)
|
||||||
|
self.hp = hitPoints
|
||||||
|
self.hpChange(quietly=1)
|
||||||
|
if justRanOutOfHp:
|
||||||
|
self.died()
|
||||||
|
return
|
||||||
|
|
||||||
|
def hpChange(self, quietly = 0):
|
||||||
|
if (not hasattr(self, 'doId')) or self.hp == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.maxHp != None:
|
||||||
|
messenger.send(self.uniqueName('hpChange'), [self.hp, self.maxHp, quietly])
|
||||||
|
if self.hp > 0:
|
||||||
|
messenger.send(self.uniqueName('positiveHP'))
|
||||||
|
|
||||||
|
def died(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getHp(self):
|
||||||
|
return self.hp
|
||||||
|
|
||||||
|
def setMaxHp(self, hitPoints):
|
||||||
|
self.maxHp = hitPoints
|
||||||
|
self.hpChange()
|
||||||
|
|
||||||
|
def getMaxHp(self):
|
||||||
|
return self.maxHp
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return Avatar.getName(self)
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
try:
|
||||||
|
self.node().setName('%s-%d' % (name, self.doId))
|
||||||
|
self.gotName = 1
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return Avatar.setName(self, name)
|
||||||
|
|
||||||
|
def showHpText(self, number, bonus = 0, scale = 1):
|
||||||
|
if self.HpTextEnabled and not self.ghostMode:
|
||||||
|
if number != 0:
|
||||||
|
if self.hpText:
|
||||||
|
self.hideHpText()
|
||||||
|
self.HpTextGenerator.setFont(OTPGlobals.getSignFont())
|
||||||
|
if number < 0:
|
||||||
|
self.HpTextGenerator.setText(str(number))
|
||||||
|
else:
|
||||||
|
self.HpTextGenerator.setText('+' + str(number))
|
||||||
|
self.HpTextGenerator.clearShadow()
|
||||||
|
self.HpTextGenerator.setAlign(TextNode.ACenter)
|
||||||
|
if bonus == 1:
|
||||||
|
r = 1.0
|
||||||
|
g = 1.0
|
||||||
|
b = 0
|
||||||
|
a = 1
|
||||||
|
elif bonus == 2:
|
||||||
|
r = 1.0
|
||||||
|
g = 0.5
|
||||||
|
b = 0
|
||||||
|
a = 1
|
||||||
|
elif number < 0:
|
||||||
|
r = 0.9
|
||||||
|
g = 0
|
||||||
|
b = 0
|
||||||
|
a = 1
|
||||||
|
else:
|
||||||
|
r = 0
|
||||||
|
g = 0.9
|
||||||
|
b = 0
|
||||||
|
a = 1
|
||||||
|
self.HpTextGenerator.setTextColor(r, g, b, a)
|
||||||
|
self.hpTextNode = self.HpTextGenerator.generate()
|
||||||
|
self.hpText = self.attachNewNode(self.hpTextNode)
|
||||||
|
self.hpText.setScale(scale)
|
||||||
|
self.hpText.setBillboardPointEye()
|
||||||
|
self.hpText.setBin('fixed', 100)
|
||||||
|
self.hpText.setPos(0, 0, self.height / 2)
|
||||||
|
seq = Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(0.85), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.hideHpText))
|
||||||
|
seq.start()
|
||||||
|
|
||||||
|
def showHpString(self, text, duration = 0.85, scale = 0.7):
|
||||||
|
if self.HpTextEnabled and not self.ghostMode:
|
||||||
|
if text != '':
|
||||||
|
if self.hpText:
|
||||||
|
self.hideHpText()
|
||||||
|
self.HpTextGenerator.setFont(OTPGlobals.getSignFont())
|
||||||
|
self.HpTextGenerator.setText(text)
|
||||||
|
self.HpTextGenerator.clearShadow()
|
||||||
|
self.HpTextGenerator.setAlign(TextNode.ACenter)
|
||||||
|
r = a = 1.0
|
||||||
|
g = b = 0.0
|
||||||
|
self.HpTextGenerator.setTextColor(r, g, b, a)
|
||||||
|
self.hpTextNode = self.HpTextGenerator.generate()
|
||||||
|
self.hpText = self.attachNewNode(self.hpTextNode)
|
||||||
|
self.hpText.setScale(scale)
|
||||||
|
self.hpText.setBillboardAxis()
|
||||||
|
self.hpText.setPos(0, 0, self.height / 2)
|
||||||
|
seq = Sequence(self.hpText.posInterval(1.0, Point3(0, 0, self.height + 1.5), blendType='easeOut'), Wait(duration), self.hpText.colorInterval(0.1, Vec4(r, g, b, 0)), Func(self.hideHpText))
|
||||||
|
seq.start()
|
||||||
|
|
||||||
|
def hideHpText(self):
|
||||||
|
if self.hpText:
|
||||||
|
taskMgr.remove(self.uniqueName('hpText'))
|
||||||
|
self.hpText.removeNode()
|
||||||
|
self.hpText = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def getStareAtNodeAndOffset(self):
|
||||||
|
return (self, Point3(0, 0, self.height))
|
||||||
|
|
||||||
|
def getAvIdName(self):
|
||||||
|
return '%s\n%s' % (self.getName(), self.doId)
|
||||||
|
|
||||||
|
def __nameTagShowAvId(self, extra = None):
|
||||||
|
self.setDisplayName(self.getAvIdName())
|
||||||
|
|
||||||
|
def __nameTagShowName(self, extra = None):
|
||||||
|
self.setDisplayName(self.getName())
|
||||||
|
|
||||||
|
def askAvOnShard(self, avId):
|
||||||
|
if base.cr.doId2do.get(avId):
|
||||||
|
messenger.send('AvOnShard%s' % avId, [True])
|
||||||
|
else:
|
||||||
|
self.sendUpdate('checkAvOnShard', [avId])
|
||||||
|
|
||||||
|
def confirmAvOnShard(self, avId, onShard = True):
|
||||||
|
messenger.send('AvOnShard%s' % avId, [onShard])
|
||||||
|
|
||||||
|
def getDialogueArray(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER)
|
||||||
|
def warp():
|
||||||
|
"""
|
||||||
|
warp the target to the invoker's current position, and rotation.
|
||||||
|
"""
|
||||||
|
invoker = spellbook.getInvoker()
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
if invoker.doId == target.doId:
|
||||||
|
return "You can't warp yourself!"
|
||||||
|
target.setPosHpr(invoker.getPos(), invoker.getHpr())
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str])
|
||||||
|
def loop(anim):
|
||||||
|
"""
|
||||||
|
animate the target using animation [anim] on the entire actor.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
target.loop(anim)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str, int, str])
|
||||||
|
def pose(anim, frame, part=None):
|
||||||
|
"""
|
||||||
|
freeze the target on frame [frame] of animation [anim] on the entire actor,
|
||||||
|
or optional [part] of the actor.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
target.pose(anim, frame, partName=part)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str, int, int, str])
|
||||||
|
def pingpong(anim, start=None, end=None, part=None):
|
||||||
|
"""
|
||||||
|
animate the target by bouncing back and forth between the start and end, or
|
||||||
|
the optional frames <start>, and [end] of animation [anim] on the entire
|
||||||
|
actor, or optional <part> of the actor.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
target.pingpong(anim, partName=part, fromFrame=start, toFrame=end)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str])
|
||||||
|
def rightHand(prop=None):
|
||||||
|
"""
|
||||||
|
parents the optional <prop> to the target's right hand node.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
rightHand = target.find('**/rightHand')
|
||||||
|
if prop is None:
|
||||||
|
for child in rightHand.getChildren():
|
||||||
|
child.removeNode()
|
||||||
|
else:
|
||||||
|
for child in rightHand.getChildren():
|
||||||
|
child.removeNode()
|
||||||
|
requestedProp = globalPropPool.getProp(prop)
|
||||||
|
requestedProp.reparentTo(rightHand)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER, types=[str])
|
||||||
|
def leftHand(prop=None):
|
||||||
|
"""
|
||||||
|
parents the optional <prop> to the target's left hand node.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
leftHand = target.find('**/leftHand')
|
||||||
|
if prop is None:
|
||||||
|
for child in leftHand.getChildren():
|
||||||
|
child.removeNode()
|
||||||
|
else:
|
||||||
|
for child in leftHand.getChildren():
|
||||||
|
child.removeNode()
|
||||||
|
requestedProp = globalPropPool.getProp(prop)
|
||||||
|
requestedProp.reparentTo(leftHand)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_PROGRAMMER, types=[])
|
||||||
|
def getPos():
|
||||||
|
"""
|
||||||
|
Return your target's position.
|
||||||
|
"""
|
||||||
|
return spellbook.getTarget().getPos()
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_PROGRAMMER, types=[int])
|
||||||
|
def setFov(fov=OTPGlobals.DefaultCameraFov):
|
||||||
|
"""
|
||||||
|
Set your field of view in-game.
|
||||||
|
"""
|
||||||
|
if fov == 0:
|
||||||
|
return 'Cannot set FOV to 0!'
|
||||||
|
base.camLens.setMinFov(fov/(4./3.))
|
||||||
|
if fov == OTPGlobals.DefaultCameraFov:
|
||||||
|
return 'Set FOV to the default.'
|
||||||
|
else:
|
||||||
|
return 'Set FOV to %s.' % fov
|
68
otp/avatar/DistributedAvatarAI.py
Executable file
68
otp/avatar/DistributedAvatarAI.py
Executable file
|
@ -0,0 +1,68 @@
|
||||||
|
from otp.ai.AIBaseGlobal import *
|
||||||
|
from otp.otpbase import OTPGlobals
|
||||||
|
from direct.distributed import DistributedNodeAI
|
||||||
|
|
||||||
|
class DistributedAvatarAI(DistributedNodeAI.DistributedNodeAI):
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
DistributedNodeAI.DistributedNodeAI.__init__(self, air)
|
||||||
|
self.hp = 0
|
||||||
|
self.maxHp = 0
|
||||||
|
|
||||||
|
def b_setName(self, name):
|
||||||
|
self.setName(name)
|
||||||
|
self.d_setName(name)
|
||||||
|
|
||||||
|
def d_setName(self, name):
|
||||||
|
self.sendUpdate('setName', [name])
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def b_setMaxHp(self, maxHp):
|
||||||
|
self.d_setMaxHp(maxHp)
|
||||||
|
self.setMaxHp(maxHp)
|
||||||
|
|
||||||
|
def d_setMaxHp(self, maxHp):
|
||||||
|
self.sendUpdate('setMaxHp', [maxHp])
|
||||||
|
|
||||||
|
def setMaxHp(self, maxHp):
|
||||||
|
self.maxHp = maxHp
|
||||||
|
|
||||||
|
def getMaxHp(self):
|
||||||
|
return self.maxHp
|
||||||
|
|
||||||
|
def b_setHp(self, hp):
|
||||||
|
self.d_setHp(hp)
|
||||||
|
self.setHp(hp)
|
||||||
|
|
||||||
|
def d_setHp(self, hp):
|
||||||
|
self.sendUpdate('setHp', [hp])
|
||||||
|
|
||||||
|
def setHp(self, hp):
|
||||||
|
self.hp = hp
|
||||||
|
|
||||||
|
def getHp(self):
|
||||||
|
return self.hp
|
||||||
|
|
||||||
|
def toonUp(self, num):
|
||||||
|
if self.hp >= self.maxHp:
|
||||||
|
return
|
||||||
|
self.hp = min(self.hp + num, self.maxHp)
|
||||||
|
self.b_setHp(self.hp)
|
||||||
|
|
||||||
|
def getRadius(self):
|
||||||
|
return OTPGlobals.AvatarDefaultRadius
|
||||||
|
|
||||||
|
def checkAvOnShard(self, avId):
|
||||||
|
self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'confirmAvOnShard', [avId, avId in self.air.doId2do])
|
||||||
|
|
||||||
|
def setParentStr(self, parentToken):
|
||||||
|
if parentToken:
|
||||||
|
senderId = self.air.getAvatarIdFromSender()
|
||||||
|
self.air.writeServerEvent('Admin chat warning', senderId, 'using setParentStr to send "%s"' % parentToken)
|
||||||
|
self.notify.warning('Admin chat warning: %s using setParentStr to send "%s"' % (senderId, parentToken))
|
||||||
|
DistributedNodeAI.DistributedNodeAI.setParentStr(self, parentToken)
|
17
otp/avatar/DistributedAvatarUD.py
Executable file
17
otp/avatar/DistributedAvatarUD.py
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from direct.distributed.DistributedObjectUD import DistributedObjectUD
|
||||||
|
|
||||||
|
class DistributedAvatarUD(DistributedObjectUD):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("DistributedAvatarUD")
|
||||||
|
|
||||||
|
def setName(self, todo0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def friendsNotify(self, todo0, todo1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def checkAvOnShard(self, todo0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def confirmAvOnShard(self, todo0, todo1):
|
||||||
|
pass
|
357
otp/avatar/DistributedPlayer.py
Executable file
357
otp/avatar/DistributedPlayer.py
Executable file
|
@ -0,0 +1,357 @@
|
||||||
|
from direct.showbase import PythonUtil
|
||||||
|
from direct.task import Task
|
||||||
|
from panda3d.core import *
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from otp.avatar import Avatar, PlayerBase, DistributedAvatar
|
||||||
|
from otp.avatar.Avatar import teleportNotify
|
||||||
|
from otp.chat import ChatGarbler, TalkAssistant
|
||||||
|
from otp.distributed.TelemetryLimited import TelemetryLimited
|
||||||
|
from otp.otpbase import OTPGlobals, OTPLocalizer
|
||||||
|
from otp.speedchat import SCDecoders
|
||||||
|
from otp.nametag.NametagConstants import *
|
||||||
|
from otp.margins.WhisperPopup import WhisperPopup
|
||||||
|
|
||||||
|
class DistributedPlayer(DistributedAvatar.DistributedAvatar, PlayerBase.PlayerBase, TelemetryLimited):
|
||||||
|
chatGarbler = ChatGarbler.ChatGarbler({'default': OTPLocalizer.ChatGarblerDefault})
|
||||||
|
|
||||||
|
def __init__(self, cr):
|
||||||
|
try:
|
||||||
|
self.DistributedPlayer_initialized
|
||||||
|
except:
|
||||||
|
self.DistributedPlayer_initialized = 1
|
||||||
|
DistributedAvatar.DistributedAvatar.__init__(self, cr)
|
||||||
|
TelemetryLimited.__init__(self)
|
||||||
|
self.__teleportAvailable = 0
|
||||||
|
self.inventory = None
|
||||||
|
self.experience = None
|
||||||
|
self.friendsList = []
|
||||||
|
self._districtWeAreGeneratedOn = None
|
||||||
|
self.DISLid = 0
|
||||||
|
self.adminAccess = 0
|
||||||
|
self.autoRun = 0
|
||||||
|
self.lastTeleportQuery = time.time()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetPlayerGenerateEvent():
|
||||||
|
return 'DistributedPlayerGenerateEvent'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetPlayerNetworkDeleteEvent():
|
||||||
|
return 'DistributedPlayerNetworkDeleteEvent'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetPlayerDeleteEvent():
|
||||||
|
return 'DistributedPlayerDeleteEvent'
|
||||||
|
|
||||||
|
def networkDelete(self):
|
||||||
|
DistributedAvatar.DistributedAvatar.networkDelete(self)
|
||||||
|
messenger.send(self.GetPlayerNetworkDeleteEvent(), [self])
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
DistributedAvatar.DistributedAvatar.disable(self)
|
||||||
|
messenger.send(self.GetPlayerDeleteEvent(), [self])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
try:
|
||||||
|
self.DistributedPlayer_deleted
|
||||||
|
except:
|
||||||
|
self.DistributedPlayer_deleted = 1
|
||||||
|
del self.experience
|
||||||
|
if self.inventory:
|
||||||
|
self.inventory.unload()
|
||||||
|
del self.inventory
|
||||||
|
DistributedAvatar.DistributedAvatar.delete(self)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
DistributedAvatar.DistributedAvatar.generate(self)
|
||||||
|
|
||||||
|
def announceGenerate(self):
|
||||||
|
DistributedAvatar.DistributedAvatar.announceGenerate(self)
|
||||||
|
messenger.send(self.GetPlayerGenerateEvent(), [self])
|
||||||
|
|
||||||
|
def setLocation(self, parentId, zoneId):
|
||||||
|
DistributedAvatar.DistributedAvatar.setLocation(self, parentId, zoneId)
|
||||||
|
if not (parentId in (0, None) and zoneId in (0, None)):
|
||||||
|
if not self.cr._isValidPlayerLocation(parentId, zoneId):
|
||||||
|
self.cr.disableDoId(self.doId)
|
||||||
|
self.cr.deleteObject(self.doId)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def isGeneratedOnDistrict(self, districtId = None):
|
||||||
|
return True # fix for the task button
|
||||||
|
if districtId is None:
|
||||||
|
return self._districtWeAreGeneratedOn is not None
|
||||||
|
else:
|
||||||
|
return self._districtWeAreGeneratedOn == districtId
|
||||||
|
return
|
||||||
|
|
||||||
|
def getArrivedOnDistrictEvent(self, districtId = None):
|
||||||
|
if districtId is None:
|
||||||
|
return 'arrivedOnDistrict'
|
||||||
|
else:
|
||||||
|
return 'arrivedOnDistrict-%s' % districtId
|
||||||
|
return
|
||||||
|
|
||||||
|
def arrivedOnDistrict(self, districtId):
|
||||||
|
curFrameTime = globalClock.getFrameTime()
|
||||||
|
if hasattr(self, 'frameTimeWeArrivedOnDistrict') and curFrameTime == self.frameTimeWeArrivedOnDistrict:
|
||||||
|
if districtId == 0 and self._districtWeAreGeneratedOn:
|
||||||
|
self.notify.warning('ignoring arrivedOnDistrict 0, since arrivedOnDistrict %d occured on the same frame' % self._districtWeAreGeneratedOn)
|
||||||
|
return
|
||||||
|
self._districtWeAreGeneratedOn = districtId
|
||||||
|
self.frameTimeWeArrivedOnDistrict = globalClock.getFrameTime()
|
||||||
|
messenger.send(self.getArrivedOnDistrictEvent(districtId))
|
||||||
|
messenger.send(self.getArrivedOnDistrictEvent())
|
||||||
|
|
||||||
|
def setLeftDistrict(self):
|
||||||
|
self._districtWeAreGeneratedOn = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def hasParentingRules(self):
|
||||||
|
if self is localAvatar:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setSystemMessage(self, aboutId, chatString, whisperType = WhisperPopup.WTSystem):
|
||||||
|
self.displayWhisper(aboutId, chatString, whisperType)
|
||||||
|
|
||||||
|
def displayWhisper(self, fromId, chatString, whisperType):
|
||||||
|
print 'Whisper type %s from %s: %s' % (whisperType, fromId, chatString)
|
||||||
|
|
||||||
|
def whisperSCTo(self, msgIndex, sendToId):
|
||||||
|
messenger.send('wakeup')
|
||||||
|
base.cr.ttsFriendsManager.d_whisperSCTo(sendToId, msgIndex)
|
||||||
|
|
||||||
|
def setWhisperSCFrom(self, fromId, msgIndex):
|
||||||
|
handle = base.cr.identifyAvatar(fromId)
|
||||||
|
if handle == None or base.localAvatar.isIgnored(fromId):
|
||||||
|
return
|
||||||
|
chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex)
|
||||||
|
if chatString:
|
||||||
|
self.displayWhisper(fromId, chatString, WhisperPopup.WTNormal)
|
||||||
|
return
|
||||||
|
|
||||||
|
def whisperSCCustomTo(self, msgIndex, sendToId):
|
||||||
|
messenger.send('wakeup')
|
||||||
|
base.cr.ttsFriendsManager.d_whisperSCCustomTo(sendToId, msgIndex)
|
||||||
|
|
||||||
|
def _isValidWhisperSource(self, source):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setWhisperSCCustomFrom(self, fromId, msgIndex):
|
||||||
|
handle = base.cr.identifyAvatar(fromId)
|
||||||
|
if handle == None:
|
||||||
|
return
|
||||||
|
if not self._isValidWhisperSource(handle):
|
||||||
|
self.notify.warning('displayWhisper from non-toon %s' % fromId)
|
||||||
|
return
|
||||||
|
if base.localAvatar.isIgnored(fromId):
|
||||||
|
return
|
||||||
|
chatString = SCDecoders.decodeSCCustomMsg(msgIndex)
|
||||||
|
if chatString:
|
||||||
|
self.displayWhisper(fromId, chatString, WhisperPopup.WTNormal)
|
||||||
|
|
||||||
|
def whisperSCEmoteTo(self, emoteId, sendToId):
|
||||||
|
messenger.send('wakeup')
|
||||||
|
base.cr.ttsFriendsManager.d_whisperSCEmoteTo(sendToId, emoteId)
|
||||||
|
|
||||||
|
def setWhisperSCEmoteFrom(self, fromId, emoteId):
|
||||||
|
handle = base.cr.identifyAvatar(fromId)
|
||||||
|
if handle == None or base.localAvatar.isIgnored(fromId):
|
||||||
|
return
|
||||||
|
chatString = SCDecoders.decodeSCEmoteWhisperMsg(emoteId, handle.getName())
|
||||||
|
if chatString:
|
||||||
|
self.displayWhisper(fromId, chatString, WhisperPopup.WTEmote)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0):
|
||||||
|
DistributedAvatar.DistributedAvatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt)
|
||||||
|
if not quiet:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setTalk(self, chat):
|
||||||
|
if not base.cr.chatAgent.verifyMessage(chat):
|
||||||
|
return
|
||||||
|
if base.localAvatar.isIgnored(self.doId):
|
||||||
|
return
|
||||||
|
if not self.understandable:
|
||||||
|
chat = self.chatGarbler.garble(self, len(chat.split(' ')))
|
||||||
|
elif base.whiteList and self.understandable < 2:
|
||||||
|
chat = base.whiteList.processThroughAll(chat, self, self.chatGarbler)
|
||||||
|
self.displayTalk(chat)
|
||||||
|
|
||||||
|
def setTalkWhisper(self, avId, chat):
|
||||||
|
if not base.cr.chatAgent.verifyMessage(chat):
|
||||||
|
return
|
||||||
|
if base.localAvatar.isIgnored(avId):
|
||||||
|
return
|
||||||
|
if not self.understandable:
|
||||||
|
chat = self.chatGarbler.garble(self, len(chat.split(' ')))
|
||||||
|
elif base.whiteList and self.understandable < 2:
|
||||||
|
chat = base.whiteList.processThroughAll(chat, self.chatGarbler)
|
||||||
|
self.displayTalkWhisper(avId, chat)
|
||||||
|
|
||||||
|
def displayTalk(self, chat):
|
||||||
|
print 'Talk: %s' % chat
|
||||||
|
|
||||||
|
def displayTalkWhisper(self, avId, chat):
|
||||||
|
print 'TalkWhisper from %s: %s' % (avId, chat)
|
||||||
|
|
||||||
|
def b_setSC(self, msgIndex):
|
||||||
|
self.setSC(msgIndex)
|
||||||
|
self.d_setSC(msgIndex)
|
||||||
|
|
||||||
|
def d_setSC(self, msgIndex):
|
||||||
|
messenger.send('wakeup')
|
||||||
|
self.sendUpdate('setSC', [msgIndex])
|
||||||
|
|
||||||
|
def setSC(self, msgIndex):
|
||||||
|
if base.localAvatar.isIgnored(self.doId):
|
||||||
|
return
|
||||||
|
chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex)
|
||||||
|
if chatString:
|
||||||
|
self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout, quiet=1)
|
||||||
|
|
||||||
|
def b_setSCCustom(self, msgIndex):
|
||||||
|
self.setSCCustom(msgIndex)
|
||||||
|
self.d_setSCCustom(msgIndex)
|
||||||
|
|
||||||
|
def d_setSCCustom(self, msgIndex):
|
||||||
|
messenger.send('wakeup')
|
||||||
|
self.sendUpdate('setSCCustom', [msgIndex])
|
||||||
|
|
||||||
|
def setSCCustom(self, msgIndex):
|
||||||
|
if base.localAvatar.isIgnored(self.doId):
|
||||||
|
return
|
||||||
|
chatString = SCDecoders.decodeSCCustomMsg(msgIndex)
|
||||||
|
if chatString:
|
||||||
|
self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout)
|
||||||
|
|
||||||
|
def b_setSCEmote(self, emoteId):
|
||||||
|
self.b_setEmoteState(emoteId, animMultiplier=self.animMultiplier)
|
||||||
|
|
||||||
|
def d_friendsNotify(self, avId, status):
|
||||||
|
self.sendUpdate('friendsNotify', [avId, status])
|
||||||
|
|
||||||
|
def friendsNotify(self, avId, status):
|
||||||
|
avatar = base.cr.identifyFriend(avId)
|
||||||
|
if avatar != None:
|
||||||
|
if status == 1:
|
||||||
|
self.setSystemMessage(avId, OTPLocalizer.WhisperNoLongerFriend % avatar.getName())
|
||||||
|
elif status == 2:
|
||||||
|
self.setSystemMessage(avId, OTPLocalizer.WhisperNowSpecialFriend % avatar.getName())
|
||||||
|
return
|
||||||
|
|
||||||
|
def d_teleportQuery(self, requesterId, sendToId = None):
|
||||||
|
lastQuery = self.lastTeleportQuery
|
||||||
|
currentQuery = time.time()
|
||||||
|
|
||||||
|
if currentQuery - lastQuery < 0.1: # Oh boy! We found a skid!
|
||||||
|
self.cr.stopReaderPollTask()
|
||||||
|
self.cr.lostConnection()
|
||||||
|
|
||||||
|
self.lastTeleportQuery = time.time()
|
||||||
|
|
||||||
|
base.cr.ttsFriendsManager.d_teleportQuery(sendToId)
|
||||||
|
|
||||||
|
def teleportQuery(self, requesterId):
|
||||||
|
avatar = base.cr.identifyFriend(requesterId)
|
||||||
|
|
||||||
|
if avatar is None:
|
||||||
|
self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
|
||||||
|
elif base.localAvatar.isIgnored(requesterId):
|
||||||
|
self.d_teleportResponse(self.doId, 2, 0, 0, 0, sendToId=requesterId)
|
||||||
|
elif hasattr(base, 'distributedParty') and ((base.distributedParty.partyInfo.isPrivate and requesterId not in base.distributedParty.inviteeIds) or base.distributedParty.isPartyEnding):
|
||||||
|
self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
|
||||||
|
elif self.__teleportAvailable and not self.ghostMode:
|
||||||
|
self.setSystemMessage(requesterId, OTPLocalizer.WhisperComingToVisit % avatar.getName())
|
||||||
|
messenger.send('teleportQuery', [avatar, self])
|
||||||
|
else:
|
||||||
|
self.setSystemMessage(requesterId, OTPLocalizer.WhisperFailedVisit % avatar.getName())
|
||||||
|
self.d_teleportResponse(self.doId, 0, 0, 0, 0, sendToId=requesterId)
|
||||||
|
|
||||||
|
def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId, sendToId):
|
||||||
|
teleportNotify.debug('sending teleportResponse%s' % ((avId, available,
|
||||||
|
shardId, hoodId, zoneId, sendToId),)
|
||||||
|
)
|
||||||
|
|
||||||
|
base.cr.ttsFriendsManager.d_teleportResponse(sendToId, available,
|
||||||
|
shardId, hoodId, zoneId
|
||||||
|
)
|
||||||
|
|
||||||
|
def teleportResponse(self, avId, available, shardId, hoodId, zoneId):
|
||||||
|
teleportNotify.debug('received teleportResponse%s' % ((avId, available,
|
||||||
|
shardId, hoodId, zoneId),)
|
||||||
|
)
|
||||||
|
|
||||||
|
messenger.send('teleportResponse', [avId, available, shardId, hoodId, zoneId])
|
||||||
|
|
||||||
|
def d_teleportGiveup(self, requesterId, sendToId):
|
||||||
|
teleportNotify.debug('sending teleportGiveup(%s) to %s' % (requesterId, sendToId))
|
||||||
|
|
||||||
|
base.cr.ttsFriendsManager.d_teleportGiveup(sendToId)
|
||||||
|
|
||||||
|
def teleportGiveup(self, requesterId):
|
||||||
|
teleportNotify.debug('received teleportGiveup(%s)' % (requesterId,))
|
||||||
|
avatar = base.cr.identifyAvatar(requesterId)
|
||||||
|
|
||||||
|
if not self._isValidWhisperSource(avatar):
|
||||||
|
self.notify.warning('teleportGiveup from non-toon %s' % requesterId)
|
||||||
|
return
|
||||||
|
|
||||||
|
if avatar is not None:
|
||||||
|
self.setSystemMessage(requesterId,
|
||||||
|
OTPLocalizer.WhisperGiveupVisit % avatar.getName()
|
||||||
|
)
|
||||||
|
|
||||||
|
def b_teleportGreeting(self, avId):
|
||||||
|
if hasattr(self, 'ghostMode') and self.ghostMode:
|
||||||
|
return
|
||||||
|
self.d_teleportGreeting(avId)
|
||||||
|
self.teleportGreeting(avId)
|
||||||
|
|
||||||
|
def d_teleportGreeting(self, avId):
|
||||||
|
self.sendUpdate('teleportGreeting', [avId])
|
||||||
|
|
||||||
|
def teleportGreeting(self, avId):
|
||||||
|
avatar = base.cr.getDo(avId)
|
||||||
|
if isinstance(avatar, Avatar.Avatar):
|
||||||
|
self.setChatAbsolute(OTPLocalizer.TeleportGreeting % avatar.getName(), CFSpeech | CFTimeout)
|
||||||
|
elif avatar is not None:
|
||||||
|
self.notify.warning('got teleportGreeting from %s referencing non-toon %s' % (self.doId, avId))
|
||||||
|
return
|
||||||
|
|
||||||
|
def setTeleportAvailable(self, available):
|
||||||
|
self.__teleportAvailable = available
|
||||||
|
|
||||||
|
def getTeleportAvailable(self):
|
||||||
|
return self.__teleportAvailable
|
||||||
|
|
||||||
|
def getFriendsList(self):
|
||||||
|
return self.friendsList
|
||||||
|
|
||||||
|
def setFriendsList(self, friendsList):
|
||||||
|
self.friendsList = friendsList
|
||||||
|
messenger.send('friendsListChanged')
|
||||||
|
Avatar.reconsiderAllUnderstandable()
|
||||||
|
|
||||||
|
def setDISLid(self, id):
|
||||||
|
self.DISLid = id
|
||||||
|
|
||||||
|
def setAdminAccess(self, access):
|
||||||
|
self.adminAccess = access
|
||||||
|
self.considerUnderstandable()
|
||||||
|
|
||||||
|
def getAdminAccess(self):
|
||||||
|
return self.adminAccess
|
||||||
|
|
||||||
|
def isAdmin(self):
|
||||||
|
return self.adminAccess >= MINIMUM_MAGICWORD_ACCESS
|
||||||
|
|
||||||
|
def setAutoRun(self, value):
|
||||||
|
self.autoRun = value
|
||||||
|
|
||||||
|
def getAutoRun(self):
|
||||||
|
return self.autoRun
|
251
otp/avatar/DistributedPlayerAI.py
Executable file
251
otp/avatar/DistributedPlayerAI.py
Executable file
|
@ -0,0 +1,251 @@
|
||||||
|
from direct.distributed.PyDatagram import PyDatagram
|
||||||
|
from direct.distributed.MsgTypes import CLIENTAGENT_EJECT
|
||||||
|
|
||||||
|
from otp.ai.AIBaseGlobal import *
|
||||||
|
from otp.ai.MagicWordGlobal import *
|
||||||
|
from otp.avatar import DistributedAvatarAI
|
||||||
|
from otp.avatar import PlayerBase
|
||||||
|
from otp.distributed import OtpDoGlobals
|
||||||
|
from otp.otpbase import OTPLocalizer
|
||||||
|
|
||||||
|
class DistributedPlayerAI(DistributedAvatarAI.DistributedAvatarAI, PlayerBase.PlayerBase):
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
DistributedAvatarAI.DistributedAvatarAI.__init__(self, air)
|
||||||
|
self.friendsList = []
|
||||||
|
self.DISLid = 0
|
||||||
|
self.adminAccess = 0
|
||||||
|
|
||||||
|
def announceGenerate(self):
|
||||||
|
DistributedAvatarAI.DistributedAvatarAI.announceGenerate(self)
|
||||||
|
self._doPlayerEnter()
|
||||||
|
|
||||||
|
def _announceArrival(self):
|
||||||
|
self.sendUpdate('arrivedOnDistrict', [self.air.districtId])
|
||||||
|
|
||||||
|
def _announceExit(self):
|
||||||
|
self.sendUpdate('arrivedOnDistrict', [0])
|
||||||
|
|
||||||
|
def _sendExitServerEvent(self):
|
||||||
|
self.air.writeServerEvent('avatarExit', self.doId, '')
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self._doPlayerExit()
|
||||||
|
DistributedAvatarAI.DistributedAvatarAI.delete(self)
|
||||||
|
|
||||||
|
def isPlayerControlled(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setLocation(self, parentId, zoneId):
|
||||||
|
DistributedAvatarAI.DistributedAvatarAI.setLocation(self, parentId, zoneId)
|
||||||
|
if self.isPlayerControlled():
|
||||||
|
if not self.air._isValidPlayerLocation(parentId, zoneId):
|
||||||
|
self.notify.info('booting player %s for doing setLocation to (%s, %s)' % (self.doId, parentId, zoneId))
|
||||||
|
self.air.writeServerEvent('suspicious', self.doId, 'invalid setLocation: (%s, %s)' % (parentId, zoneId))
|
||||||
|
self.requestDelete()
|
||||||
|
|
||||||
|
def _doPlayerEnter(self):
|
||||||
|
self.incrementPopulation()
|
||||||
|
self._announceArrival()
|
||||||
|
|
||||||
|
def _doPlayerExit(self):
|
||||||
|
self._announceExit()
|
||||||
|
self.decrementPopulation()
|
||||||
|
|
||||||
|
def incrementPopulation(self):
|
||||||
|
self.air.incrementPopulation()
|
||||||
|
|
||||||
|
def decrementPopulation(self):
|
||||||
|
simbase.air.decrementPopulation()
|
||||||
|
|
||||||
|
def d_setMaxHp(self, maxHp):
|
||||||
|
DistributedAvatarAI.DistributedAvatarAI.d_setMaxHp(self, maxHp)
|
||||||
|
self.air.writeServerEvent('setMaxHp', self.doId, '%s' % maxHp)
|
||||||
|
|
||||||
|
def d_setSystemMessage(self, aboutId, chatString):
|
||||||
|
self.sendUpdate('setSystemMessage', [aboutId, chatString])
|
||||||
|
|
||||||
|
def d_friendsNotify(self, avId, status):
|
||||||
|
self.sendUpdate('friendsNotify', [avId, status])
|
||||||
|
|
||||||
|
def friendsNotify(self, avId, status):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setDISLid(self, id):
|
||||||
|
self.DISLid = id
|
||||||
|
|
||||||
|
def getDISLid(self):
|
||||||
|
return self.DISLid
|
||||||
|
|
||||||
|
def d_setFriendsList(self, friendsList):
|
||||||
|
self.sendUpdate('setFriendsList', [friendsList])
|
||||||
|
|
||||||
|
def setFriendsList(self, friendsList):
|
||||||
|
self.friendsList = friendsList
|
||||||
|
self.notify.debug('setting friends list to %s' % self.friendsList)
|
||||||
|
|
||||||
|
def getFriendsList(self):
|
||||||
|
return self.friendsList
|
||||||
|
|
||||||
|
def setAdminAccess(self, access):
|
||||||
|
self.adminAccess = access
|
||||||
|
|
||||||
|
def d_setAdminAccess(self, access):
|
||||||
|
self.sendUpdate('setAdminAccess', [access])
|
||||||
|
|
||||||
|
def b_setAdminAccess(self, access):
|
||||||
|
self.setAdminAccess(access)
|
||||||
|
self.d_setAdminAccess(access)
|
||||||
|
|
||||||
|
def getAdminAccess(self):
|
||||||
|
return self.adminAccess
|
||||||
|
|
||||||
|
def isAdmin(self):
|
||||||
|
return self.adminAccess >= MINIMUM_MAGICWORD_ACCESS
|
||||||
|
|
||||||
|
def extendFriendsList(self, friendId):
|
||||||
|
if friendId in self.friendsList:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.friendsList.append(friendId)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[str])
|
||||||
|
def system(message):
|
||||||
|
"""
|
||||||
|
Broadcast a <message> to the game server.
|
||||||
|
"""
|
||||||
|
message = 'ADMIN: ' + message
|
||||||
|
dclass = simbase.air.dclassesByName['ClientServicesManager']
|
||||||
|
dg = dclass.aiFormatUpdate('systemMessage',
|
||||||
|
OtpDoGlobals.OTP_DO_ID_CLIENT_SERVICES_MANAGER,
|
||||||
|
10, 1000000, [message])
|
||||||
|
simbase.air.send(dg)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[int])
|
||||||
|
def maintenance(minutes):
|
||||||
|
"""
|
||||||
|
Initiate the maintenance message sequence. It will last for the specified
|
||||||
|
amount of <minutes>.
|
||||||
|
"""
|
||||||
|
def disconnect(task):
|
||||||
|
dg = PyDatagram()
|
||||||
|
dg.addServerHeader(10, simbase.air.ourChannel, CLIENTAGENT_EJECT)
|
||||||
|
dg.addUint16(154)
|
||||||
|
dg.addString('Toontown Stride is now closed for maintenance.')
|
||||||
|
simbase.air.send(dg)
|
||||||
|
return Task.done
|
||||||
|
|
||||||
|
def countdown(minutes):
|
||||||
|
if minutes > 0:
|
||||||
|
system(OTPLocalizer.CRMaintenanceCountdownMessage % minutes)
|
||||||
|
else:
|
||||||
|
system(OTPLocalizer.CRMaintenanceMessage)
|
||||||
|
taskMgr.doMethodLater(10, disconnect, 'maintenance-disconnection')
|
||||||
|
if minutes <= 5:
|
||||||
|
next = 60
|
||||||
|
minutes -= 1
|
||||||
|
elif minutes % 5:
|
||||||
|
next = 60 * (minutes%5)
|
||||||
|
minutes -= minutes % 5
|
||||||
|
else:
|
||||||
|
next = 300
|
||||||
|
minutes -= 5
|
||||||
|
if minutes >= 0:
|
||||||
|
taskMgr.doMethodLater(next, countdown, 'maintenance-task',
|
||||||
|
extraArgs=[minutes])
|
||||||
|
|
||||||
|
|
||||||
|
countdown(minutes)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_SYSTEM_ADMINISTRATOR, types=[str, str])
|
||||||
|
def accessLevel(accessLevel, storage='PERSISTENT'):
|
||||||
|
"""
|
||||||
|
Modify the target's access level.
|
||||||
|
"""
|
||||||
|
accessName2Id = {
|
||||||
|
'user': CATEGORY_USER.defaultAccess,
|
||||||
|
'u': CATEGORY_USER.defaultAccess,
|
||||||
|
'communitymanager': CATEGORY_COMMUNITY_MANAGER.defaultAccess,
|
||||||
|
'community': CATEGORY_COMMUNITY_MANAGER.defaultAccess,
|
||||||
|
'c': CATEGORY_COMMUNITY_MANAGER.defaultAccess,
|
||||||
|
'moderator': CATEGORY_MODERATOR.defaultAccess,
|
||||||
|
'mod': CATEGORY_MODERATOR.defaultAccess,
|
||||||
|
'm': CATEGORY_MODERATOR.defaultAccess,
|
||||||
|
'creative': CATEGORY_CREATIVE.defaultAccess,
|
||||||
|
'creativity': CATEGORY_CREATIVE.defaultAccess,
|
||||||
|
'c': CATEGORY_CREATIVE.defaultAccess,
|
||||||
|
'programmer': CATEGORY_PROGRAMMER.defaultAccess,
|
||||||
|
'coder': CATEGORY_PROGRAMMER.defaultAccess,
|
||||||
|
'p': CATEGORY_PROGRAMMER.defaultAccess,
|
||||||
|
'administrator': CATEGORY_ADMINISTRATOR.defaultAccess,
|
||||||
|
'admin': CATEGORY_ADMINISTRATOR.defaultAccess,
|
||||||
|
'a': CATEGORY_ADMINISTRATOR.defaultAccess,
|
||||||
|
'systemadministrator': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
'systemadmin': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
'sysadministrator': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
'sysadmin': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
'system': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
'sys': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess,
|
||||||
|
's': CATEGORY_SYSTEM_ADMINISTRATOR.defaultAccess
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
accessLevel = int(accessLevel)
|
||||||
|
except:
|
||||||
|
if accessLevel not in accessName2Id:
|
||||||
|
return 'Invalid access level!'
|
||||||
|
accessLevel = accessName2Id[accessLevel]
|
||||||
|
if accessLevel not in accessName2Id.values():
|
||||||
|
return 'Invalid access level!'
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
invoker = spellbook.getInvoker()
|
||||||
|
if invoker == target:
|
||||||
|
return "You can't set your own access level!"
|
||||||
|
if not accessLevel < invoker.getAdminAccess():
|
||||||
|
return "The target's access level must be lower than yours!"
|
||||||
|
if target.getAdminAccess() == accessLevel:
|
||||||
|
return "%s's access level is already %d!" % (target.getName(), accessLevel)
|
||||||
|
target.b_setAdminAccess(accessLevel)
|
||||||
|
temporary = storage.upper() in ('SESSION', 'TEMP', 'TEMPORARY')
|
||||||
|
if not temporary:
|
||||||
|
target.air.dbInterface.updateObject(
|
||||||
|
target.air.dbId,
|
||||||
|
target.getDISLid(),
|
||||||
|
target.air.dclassesByName['AccountAI'],
|
||||||
|
{'ADMIN_ACCESS': accessLevel})
|
||||||
|
if not temporary:
|
||||||
|
target.d_setSystemMessage(0, '%s set your access level to %d!' % (invoker.getName(), accessLevel))
|
||||||
|
return "%s's access level has been set to %d." % (target.getName(), accessLevel)
|
||||||
|
else:
|
||||||
|
target.d_setSystemMessage(0, '%s set your access level to %d temporarily!' % (invoker.getName(), accessLevel))
|
||||||
|
return "%s's access level has been set to %d temporarily." % (target.getName(), accessLevel)
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER)
|
||||||
|
def disableGM():
|
||||||
|
"""
|
||||||
|
Temporarily disable GM features.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
|
||||||
|
if hasattr(target, 'oldAccess'):
|
||||||
|
return 'GM features are already disabled!\nTo enable, use ~enableGM.'
|
||||||
|
|
||||||
|
if not target.isAdmin():
|
||||||
|
return 'Target is not an admin!'
|
||||||
|
|
||||||
|
target.oldAccess = target.adminAccess
|
||||||
|
target.d_setAdminAccess(100)
|
||||||
|
return 'GM features are disabled!'
|
||||||
|
|
||||||
|
@magicWord(category=CATEGORY_COMMUNITY_MANAGER)
|
||||||
|
def enableGM():
|
||||||
|
"""
|
||||||
|
Enable GM features.
|
||||||
|
"""
|
||||||
|
target = spellbook.getTarget()
|
||||||
|
|
||||||
|
if not hasattr(target, 'oldAccess'):
|
||||||
|
return 'GM features are not disabled!'
|
||||||
|
|
||||||
|
target.d_setAdminAccess(target.oldAccess)
|
||||||
|
del target.oldAccess
|
||||||
|
return 'GM features are enabled!'
|
22
otp/avatar/Emote.py
Executable file
22
otp/avatar/Emote.py
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
from otp.otpbase import OTPLocalizer
|
||||||
|
import types
|
||||||
|
|
||||||
|
class Emote:
|
||||||
|
EmoteClear = -1
|
||||||
|
EmoteEnableStateChanged = 'EmoteEnableStateChanged'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.emoteFunc = None
|
||||||
|
return
|
||||||
|
|
||||||
|
def isEnabled(self, index):
|
||||||
|
if isinstance(index, types.StringType):
|
||||||
|
index = OTPLocalizer.EmoteFuncDict[index]
|
||||||
|
if self.emoteFunc == None:
|
||||||
|
return 0
|
||||||
|
elif self.emoteFunc[index][1] == 0:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
globalEmote = None
|
1177
otp/avatar/LocalAvatar.py
Executable file
1177
otp/avatar/LocalAvatar.py
Executable file
File diff suppressed because it is too large
Load diff
7
otp/avatar/PlayerBase.py
Executable file
7
otp/avatar/PlayerBase.py
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
class PlayerBase:
|
||||||
|
|
||||||
|
def atLocation(self, locationId):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getLocation(self):
|
||||||
|
return []
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue