فهرست منبع

Merge branch 'master' into deploy-ng

Mitchell Stokes 7 سال پیش
والد
کامیت
ebe2588a50

+ 48 - 57
direct/src/showbase/PythonUtil.py

@@ -10,7 +10,7 @@ __all__ = ['indent',
 'bound', 'clamp', 'lerp', 'average', 'addListsByValue',
 'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
 'findPythonModule', 'mostDerivedLast',
-'weightedChoice', 'randFloat', 'normalDistrib',
+'clampScalar', 'weightedChoice', 'randFloat', 'normalDistrib',
 'weightedRand', 'randUint31', 'randInt32',
 'SerialNumGen', 'serialNum', 'uniqueName', 'Enum', 'Singleton',
 'SingletonError', 'printListEnum', 'safeRepr',
@@ -178,27 +178,6 @@ class Queue:
     def __len__(self):
         return len(self.__list)
 
-if __debug__ and __name__ == '__main__':
-    q = Queue()
-    assert q.isEmpty()
-    q.clear()
-    assert q.isEmpty()
-    q.push(10)
-    assert not q.isEmpty()
-    q.push(20)
-    assert not q.isEmpty()
-    assert len(q) == 2
-    assert q.front() == 10
-    assert q.back() == 20
-    assert q.top() == 10
-    assert q.top() == 10
-    assert q.pop() == 10
-    assert len(q) == 1
-    assert not q.isEmpty()
-    assert q.pop() == 20
-    assert len(q) == 0
-    assert q.isEmpty()
-
 
 def indent(stream, numIndents, str):
     """
@@ -1130,6 +1109,23 @@ def findPythonModule(module):
 
     return None
 
+def clampScalar(value, a, b):
+    # calling this ought to be faster than calling both min and max
+    if a < b:
+        if value < a:
+            return a
+        elif value > b:
+            return b
+        else:
+            return value
+    else:
+        if value < b:
+            return b
+        elif value > a:
+            return a
+        else:
+            return value
+
 def weightedChoice(choiceList, rng=random.random, sum=None):
     """given a list of (weight, item) pairs, chooses an item based on the
     weights. rng must return 0..1. if you happen to have the sum of the
@@ -2313,36 +2309,6 @@ def flywheel(*args, **kArgs):
         pass
     return flywheel
 
-if __debug__ and __name__ == '__main__':
-    f = flywheel(['a','b','c','d'], countList=[11,20,3,4])
-    obj2count = {}
-    for obj in f:
-        obj2count.setdefault(obj, 0)
-        obj2count[obj] += 1
-    assert obj2count['a'] == 11
-    assert obj2count['b'] == 20
-    assert obj2count['c'] == 3
-    assert obj2count['d'] == 4
-
-    f = flywheel([1,2,3,4], countFunc=lambda x: x*2)
-    obj2count = {}
-    for obj in f:
-        obj2count.setdefault(obj, 0)
-        obj2count[obj] += 1
-    assert obj2count[1] == 2
-    assert obj2count[2] == 4
-    assert obj2count[3] == 6
-    assert obj2count[4] == 8
-
-    f = flywheel([1,2,3,4], countFunc=lambda x: x, scale = 3)
-    obj2count = {}
-    for obj in f:
-        obj2count.setdefault(obj, 0)
-        obj2count[obj] += 1
-    assert obj2count[1] == 1 * 3
-    assert obj2count[2] == 2 * 3
-    assert obj2count[3] == 3 * 3
-    assert obj2count[4] == 4 * 3
 
 if __debug__:
     def quickProfile(name="unnamed"):
@@ -2687,11 +2653,36 @@ def unescapeHtmlString(s):
         result += char
     return result
 
-if __debug__ and __name__ == '__main__':
-    assert unescapeHtmlString('asdf') == 'asdf'
-    assert unescapeHtmlString('as+df') == 'as df'
-    assert unescapeHtmlString('as%32df') == 'as2df'
-    assert unescapeHtmlString('asdf%32') == 'asdf2'
+class PriorityCallbacks:
+    """ manage a set of prioritized callbacks, and allow them to be invoked in order of priority """
+    def __init__(self):
+        self._callbacks = []
+
+    def clear(self):
+        del self._callbacks[:]
+
+    def add(self, callback, priority=None):
+        if priority is None:
+            priority = 0
+        callbacks = self._callbacks
+        lo = 0
+        hi = len(callbacks)
+        while lo < hi:
+            mid = (lo + hi) // 2
+            if priority < callbacks[mid][0]:
+                hi = mid
+            else:
+                lo = mid + 1
+        item = (priority, callback)
+        callbacks.insert(lo, item)
+        return item
+
+    def remove(self, item):
+        self._callbacks.remove(item)
+
+    def __call__(self):
+        for priority, callback in self._callbacks:
+            callback()
 
 builtins.Functor = Functor
 builtins.Stack = Stack

+ 3 - 2
makepanda/makepanda.py

@@ -615,6 +615,7 @@ if (COMPILER == "MSVC"):
     PkgDisable("EGL")
     PkgDisable("CARBON")
     PkgDisable("COCOA")
+    DefSymbol("FLEX", "YY_NO_UNISTD_H")
     if (PkgSkip("PYTHON")==0):
         IncDirectory("ALWAYS", SDK["PYTHON"] + "/include")
         LibDirectory("ALWAYS", SDK["PYTHON"] + "/libs")
@@ -890,7 +891,7 @@ if (COMPILER=="GCC"):
         SmartPkgEnable("EIGEN",     "eigen3",    (), ("Eigen/Dense",), target_pkg = 'ALWAYS')
         SmartPkgEnable("ARTOOLKIT", "",          ("AR"), "AR/ar.h")
         SmartPkgEnable("FCOLLADA",  "",          ChooseLib(fcollada_libs, "FCOLLADA"), ("FCollada", "FCollada/FCollada.h"))
-        SmartPkgEnable("ASSIMP",    "assimp", ("assimp"), "assimp")
+        SmartPkgEnable("ASSIMP",    "",          ("assimp"), "assimp")
         SmartPkgEnable("FFMPEG",    ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h"))
         SmartPkgEnable("SWSCALE",   "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
         SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
@@ -1460,7 +1461,7 @@ def CompileBison(wobj, wsrc, opts):
         CopyFile(wdsth, GetOutputDir()+"/tmp/"+ifile+".h")
 
     # Finally, compile the generated source file.
-    CompileCxx(wobj,wdstc,opts)
+    CompileCxx(wobj, wdstc, opts + ["FLEX"])
 
 ########################################################################
 ##

+ 6 - 1
panda/src/bullet/bulletWorld.cxx

@@ -1052,7 +1052,7 @@ BulletPersistentManifold *BulletWorld::
 get_manifold(int idx) const {
   LightMutexHolder holder(get_global_lock());
 
-  nassertr(idx < get_num_manifolds(), NULL);
+  nassertr(idx < _dispatcher->getNumManifolds(), NULL);
 
   btPersistentManifold *ptr = _dispatcher->getManifoldByIndexInternal(idx);
   return (ptr) ? new BulletPersistentManifold(ptr) : NULL;
@@ -1186,7 +1186,12 @@ tick_callback(btDynamicsWorld *world, btScalar timestep) {
   CallbackObject *obj = w->_tick_callback_obj;
   if (obj) {
     BulletTickCallbackData cbdata(timestep);
+    // Release the global lock that we are holding during the tick callback
+    // and allow interactions with bullet world in the user callback
+    get_global_lock().release();
     obj->do_callback(&cbdata);
+    // Acquire the global lock again and protect the execution
+    get_global_lock().acquire();
   }
 }
 

+ 17 - 0
panda/src/downloader/httpChannel.I

@@ -426,6 +426,23 @@ get_max_updates_per_second() const {
   return _max_updates_per_second;
 }
 
+/**
+ * Specifies the Content-Type header, useful for applications that require
+ * different types of content, such as JSON.
+ */
+INLINE void HTTPChannel::
+set_content_type(string content_type) {
+  _content_type = content_type;
+}
+
+/**
+ * Returns the value of the Content-Type header.
+ */
+INLINE string HTTPChannel::
+get_content_type() const {
+  return _content_type;
+}
+
 /**
  * This may be called immediately after a call to get_document() or some
  * related function to specify the expected size of the document we are

+ 2 - 1
panda/src/downloader/httpChannel.cxx

@@ -100,6 +100,7 @@ HTTPChannel(HTTPClient *client) :
   _response_type = RT_none;
   _http_version = _client->get_http_version();
   _http_version_string = _client->get_http_version_string();
+  _content_type = "application/x-www-form-urlencoded";
   _state = S_new;
   _done_state = S_new;
   _started_download = false;
@@ -3624,7 +3625,7 @@ make_header() {
 
   if (!_body.empty()) {
     stream
-      << "Content-Type: application/x-www-form-urlencoded\r\n"
+      << "Content-Type: " << _content_type << "\r\n"
       << "Content-Length: " << _body.length() << "\r\n";
   }
 

+ 4 - 0
panda/src/downloader/httpChannel.h

@@ -143,6 +143,9 @@ PUBLISHED:
   INLINE void set_max_updates_per_second(double max_updates_per_second);
   INLINE double get_max_updates_per_second() const;
 
+  INLINE void set_content_type(string content_type);
+  INLINE string get_content_type() const;
+
   INLINE void set_expected_file_size(size_t file_size);
   streamsize get_file_size() const;
   INLINE bool is_file_size_known() const;
@@ -336,6 +339,7 @@ private:
   string request_path;
   string _header;
   string _body;
+  string _content_type;
   bool _want_ssl;
   bool _proxy_serves_document;
   bool _proxy_tunnel_now;

+ 2 - 1
panda/src/event/asyncTaskChain.cxx

@@ -477,6 +477,7 @@ do_remove(AsyncTask *task, bool upon_death) {
     {
       int index = find_task_on_heap(_sleeping, task);
       nassertr(index != -1, false);
+      PT(AsyncTask) hold_task = task;
       _sleeping.erase(_sleeping.begin() + index);
       make_heap(_sleeping.begin(), _sleeping.end(), AsyncTaskSortWakeTime());
       cleanup_task(task, upon_death, false);
@@ -486,6 +487,7 @@ do_remove(AsyncTask *task, bool upon_death) {
   case AsyncTask::S_active:
     {
       // Active, but not being serviced, easy.
+      PT(AsyncTask) hold_task = task;
       int index = find_task_on_heap(_active, task);
       if (index != -1) {
         _active.erase(_active.begin() + index);
@@ -769,7 +771,6 @@ cleanup_task(AsyncTask *task, bool upon_death, bool clean_exit) {
   }
 
   nassertv(task->_chain == this);
-  PT(AsyncTask) hold_task = task;
 
   task->_state = AsyncTask::S_inactive;
   task->_chain = nullptr;

+ 19 - 9
panda/src/putil/datagramInputFile.cxx

@@ -138,35 +138,45 @@ get_datagram(Datagram &data) {
     return true;
   }
 
-  streamsize num_bytes = (streamsize)num_bytes_32;
+  size_t num_bytes = (size_t)num_bytes_32;
   if (num_bytes_32 == (uint32_t)-1) {
     // Another special case for a value larger than 32 bits.
-    num_bytes = reader.get_uint64();
-  }
+    uint64_t num_bytes_64 = reader.get_uint64();
 
-  // Make sure we have a reasonable datagram size for putting into memory.
-  nassertr(num_bytes == (size_t)num_bytes, false);
+    if (_in->fail() || _in->eof()) {
+      _error = true;
+      return false;
+    }
+
+    num_bytes = (size_t)num_bytes_64;
+
+    // Make sure we have a reasonable datagram size for putting into memory.
+    if (num_bytes_64 != (uint64_t)num_bytes) {
+      _error = true;
+      return false;
+    }
+  }
 
   // Now, read the datagram itself. We construct an empty datagram, use
   // pad_bytes to make it big enough, and read *directly* into the datagram's
   // internal buffer. Doing this saves us a copy operation.
   data = Datagram();
 
-  streamsize bytes_read = 0;
+  size_t bytes_read = 0;
   while (bytes_read < num_bytes) {
-    streamsize bytes_left = num_bytes - bytes_read;
+    size_t bytes_left = num_bytes - bytes_read;
 
     // Hold up a second - datagrams >4MB are pretty large by bam/network
     // standards. Let's take it 4MB at a time just in case the length is
     // corrupt, so we don't allocate potentially a few GBs of RAM only to
     // find a truncated file.
-    bytes_left = min(bytes_left, (streamsize)4*1024*1024);
+    bytes_left = min(bytes_left, (size_t)4*1024*1024);
 
     PTA_uchar buffer = data.modify_array();
     buffer.resize(buffer.size() + bytes_left);
     unsigned char *ptr = &buffer.p()[bytes_read];
 
-    _in->read((char *)ptr, bytes_left);
+    _in->read((char *)ptr, (streamsize)bytes_left);
     if (_in->fail() || _in->eof()) {
       _error = true;
       return false;

+ 2 - 2
pandatool/src/flt/fltBeadID.cxx

@@ -78,8 +78,8 @@ extract_record(FltRecordReader &reader) {
 bool FltBeadID::
 extract_ancillary(FltRecordReader &reader) {
   if (reader.get_opcode() == FO_long_id) {
-    vector_uchar s = reader.get_iterator().get_remaining_bytes();
-    _id.assign((const char *)s.data(), strnlen((const char *)s.data(), s.size()));
+    DatagramIterator &di = reader.get_iterator();
+    _id = di.get_fixed_string(di.get_remaining_size());
     return true;
   }
 

+ 2 - 2
pandatool/src/flt/fltRecord.cxx

@@ -621,8 +621,8 @@ extract_record(FltRecordReader &) {
 bool FltRecord::
 extract_ancillary(FltRecordReader &reader) {
   if (reader.get_opcode() == FO_comment) {
-    vector_uchar s = reader.get_iterator().get_remaining_bytes();
-    _comment.assign((const char *)s.data(), strnlen((const char *)s.data(), s.size()));
+    DatagramIterator &di = reader.get_iterator();
+    _comment = di.get_fixed_string(di.get_remaining_size());
     return true;
   }
 

+ 2 - 2
tests/bullet/test_bullet_bam.py

@@ -88,8 +88,8 @@ def test_minkowski_sum_shape():
     assert type(shape) is type(shape2)
     assert shape.margin == shape2.margin
     assert shape.name == shape2.name
-    assert shape.transform_a.compare_to(shape2.transform_a, True) == 0
-    assert shape.transform_b.compare_to(shape2.transform_b, True) == 0
+    assert shape.transform_a.mat.compare_to(shape2.transform_a.mat, 0.001) == 0
+    assert shape.transform_b.mat.compare_to(shape2.transform_b.mat, 0.001) == 0
     assert type(shape.shape_a) == type(shape2.shape_a)
     assert type(shape.shape_b) == type(shape2.shape_b)
 

+ 105 - 0
tests/showbase/test_PythonUtil.py

@@ -0,0 +1,105 @@
+from direct.showbase import PythonUtil
+
+
+def test_queue():
+    q = PythonUtil.Queue()
+    assert q.isEmpty()
+    q.clear()
+    assert q.isEmpty()
+    q.push(10)
+    assert not q.isEmpty()
+    q.push(20)
+    assert not q.isEmpty()
+    assert len(q) == 2
+    assert q.front() == 10
+    assert q.back() == 20
+    assert q.top() == 10
+    assert q.top() == 10
+    assert q.pop() == 10
+    assert len(q) == 1
+    assert not q.isEmpty()
+    assert q.pop() == 20
+    assert len(q) == 0
+    assert q.isEmpty()
+
+
+def test_flywheel():
+    f = PythonUtil.flywheel(['a','b','c','d'], countList=[11,20,3,4])
+    obj2count = {}
+    for obj in f:
+        obj2count.setdefault(obj, 0)
+        obj2count[obj] += 1
+    assert obj2count['a'] == 11
+    assert obj2count['b'] == 20
+    assert obj2count['c'] == 3
+    assert obj2count['d'] == 4
+
+    f = PythonUtil.flywheel([1,2,3,4], countFunc=lambda x: x*2)
+    obj2count = {}
+    for obj in f:
+        obj2count.setdefault(obj, 0)
+        obj2count[obj] += 1
+    assert obj2count[1] == 2
+    assert obj2count[2] == 4
+    assert obj2count[3] == 6
+    assert obj2count[4] == 8
+
+    f = PythonUtil.flywheel([1,2,3,4], countFunc=lambda x: x, scale = 3)
+    obj2count = {}
+    for obj in f:
+        obj2count.setdefault(obj, 0)
+        obj2count[obj] += 1
+    assert obj2count[1] == 1 * 3
+    assert obj2count[2] == 2 * 3
+    assert obj2count[3] == 3 * 3
+    assert obj2count[4] == 4 * 3
+
+
+def test_unescape_html_string():
+    assert PythonUtil.unescapeHtmlString('asdf') == 'asdf'
+    assert PythonUtil.unescapeHtmlString('as+df') == 'as df'
+    assert PythonUtil.unescapeHtmlString('as%32df') == 'as2df'
+    assert PythonUtil.unescapeHtmlString('asdf%32') == 'asdf2'
+
+
+def test_priority_callbacks():
+    l = []
+    def a(l=l):
+        l.append('a')
+    def b(l=l):
+        l.append('b')
+    def c(l=l):
+        l.append('c')
+
+    pc = PythonUtil.PriorityCallbacks()
+    pc.add(a)
+    pc()
+    assert l == ['a']
+
+    del l[:]
+    bItem = pc.add(b)
+    pc()
+    assert 'a' in l
+    assert 'b' in l
+    assert len(l) == 2
+
+    del l[:]
+    pc.remove(bItem)
+    pc()
+    assert l == ['a']
+
+    del l[:]
+    pc.add(c, 2)
+    bItem = pc.add(b, 10)
+    pc()
+    assert l == ['a', 'c', 'b']
+
+    del l[:]
+    pc.remove(bItem)
+    pc()
+    assert l == ['a', 'c']
+
+    del l[:]
+    pc.clear()
+    pc()
+    assert len(l) == 0