mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-30 23:51:05 +00:00
e021fba5e1
- Instead of tying NoDelay behavior to OF_JustSpawned, use a new actor flag, MF7_HANDLENODELAY. This only gets cleared once it has actually been checked by Tick(). This is necessary because freeze mode delays the initial run of Tick() past the initial spawn, so OF_JustSpawned will no longer be set when it does the initial tick. - Delay NoDelay processing if an actor is spawned dormant. Actors spawned dormant have Deactivate() called before they tick, so MF7_HANDLENODELAY will remain set as long as an actor is dormant. This allows the NoDelay handling to occur as expected once it is activated.
599 lines
15 KiB
C++
599 lines
15 KiB
C++
/*
|
|
** dobject.h
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2008 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#ifndef __DOBJECT_H__
|
|
#define __DOBJECT_H__
|
|
|
|
#include <stdlib.h>
|
|
#include "doomtype.h"
|
|
|
|
struct PClass;
|
|
|
|
class FArchive;
|
|
|
|
class DObject;
|
|
class DArgs;
|
|
class DCanvas;
|
|
class DConsoleCommand;
|
|
class DConsoleAlias;
|
|
class DSeqNode;
|
|
class DSeqActorNode;
|
|
class DSeqPolyNode;
|
|
class DSeqSectorNode;
|
|
class DThinker;
|
|
class AActor;
|
|
class DPolyAction;
|
|
class DMovePoly;
|
|
class DPolyDoor;
|
|
class DRotatePoly;
|
|
class DPusher;
|
|
class DScroller;
|
|
class DSectorEffect;
|
|
class DLighting;
|
|
class DFireFlicker;
|
|
class DFlicker;
|
|
class DGlow;
|
|
class DGlow2;
|
|
class DLightFlash;
|
|
class DPhased;
|
|
class DStrobe;
|
|
class DMover;
|
|
class DElevator;
|
|
class DMovingCeiling;
|
|
class DCeiling;
|
|
class DDoor;
|
|
class DMovingFloor;
|
|
class DFloor;
|
|
class DFloorWaggle;
|
|
class DPlat;
|
|
class DPillar;
|
|
|
|
struct FActorInfo;
|
|
|
|
enum EMetaType
|
|
{
|
|
META_Int, // An int
|
|
META_Fixed, // A fixed point number
|
|
META_String, // A string
|
|
};
|
|
|
|
class FMetaData
|
|
{
|
|
private:
|
|
FMetaData (EMetaType type, uint32 id) : Type(type), ID(id) {}
|
|
|
|
FMetaData *Next;
|
|
EMetaType Type;
|
|
uint32 ID;
|
|
union
|
|
{
|
|
int Int;
|
|
char *String;
|
|
fixed_t Fixed;
|
|
} Value;
|
|
|
|
friend class FMetaTable;
|
|
};
|
|
|
|
class FMetaTable
|
|
{
|
|
public:
|
|
FMetaTable() : Meta(NULL) {}
|
|
FMetaTable(const FMetaTable &other);
|
|
~FMetaTable();
|
|
FMetaTable &operator = (const FMetaTable &other);
|
|
|
|
void SetMetaInt (uint32 id, int parm);
|
|
void SetMetaFixed (uint32 id, fixed_t parm);
|
|
void SetMetaString (uint32 id, const char *parm); // The string is copied
|
|
|
|
int GetMetaInt (uint32 id, int def=0) const;
|
|
fixed_t GetMetaFixed (uint32 id, fixed_t def=0) const;
|
|
const char *GetMetaString (uint32 id) const;
|
|
|
|
FMetaData *FindMeta (EMetaType type, uint32 id) const;
|
|
|
|
private:
|
|
FMetaData *Meta;
|
|
FMetaData *FindMetaDef (EMetaType type, uint32 id);
|
|
void FreeMeta ();
|
|
void CopyMeta (const FMetaTable *other);
|
|
};
|
|
|
|
#define RUNTIME_TYPE(object) (object->GetClass()) // Passed an object, returns the type of that object
|
|
#define RUNTIME_CLASS(cls) (&cls::_StaticType) // Passed a class name, returns a PClass representing that class
|
|
#define NATIVE_TYPE(object) (object->StaticType()) // Passed an object, returns the type of the C++ class representing the object
|
|
|
|
struct ClassReg
|
|
{
|
|
PClass *MyClass;
|
|
const char *Name;
|
|
PClass *ParentType;
|
|
unsigned int SizeOf;
|
|
const size_t *Pointers;
|
|
void (*ConstructNative)(void *);
|
|
|
|
void RegisterClass() const;
|
|
};
|
|
|
|
enum EInPlace { EC_InPlace };
|
|
|
|
#define DECLARE_ABSTRACT_CLASS(cls,parent) \
|
|
public: \
|
|
static PClass _StaticType; \
|
|
virtual PClass *StaticType() const { return &_StaticType; } \
|
|
static ClassReg RegistrationInfo, *RegistrationInfoPtr; \
|
|
private: \
|
|
typedef parent Super; \
|
|
typedef cls ThisClass;
|
|
|
|
#define DECLARE_CLASS(cls,parent) \
|
|
DECLARE_ABSTRACT_CLASS(cls,parent) \
|
|
private: static void InPlaceConstructor (void *mem);
|
|
|
|
#define HAS_OBJECT_POINTERS \
|
|
static const size_t PointerOffsets[];
|
|
|
|
// Taking the address of a field in an object at address 1 instead of
|
|
// address 0 keeps GCC from complaining about possible misuse of offsetof.
|
|
#define DECLARE_POINTER(field) (size_t)&((ThisClass*)1)->field - 1,
|
|
#define END_POINTERS ~(size_t)0 };
|
|
|
|
#if defined(_MSC_VER)
|
|
# pragma data_seg(".creg$u")
|
|
# pragma data_seg()
|
|
# define _DECLARE_TI(cls) __declspec(allocate(".creg$u")) ClassReg *cls::RegistrationInfoPtr = &cls::RegistrationInfo;
|
|
#else
|
|
# define _DECLARE_TI(cls) ClassReg *cls::RegistrationInfoPtr __attribute__((section(SECTION_CREG))) = &cls::RegistrationInfo;
|
|
#endif
|
|
|
|
#define _IMP_PCLASS(cls,ptrs,create) \
|
|
PClass cls::_StaticType; \
|
|
ClassReg cls::RegistrationInfo = {\
|
|
RUNTIME_CLASS(cls), \
|
|
#cls, \
|
|
RUNTIME_CLASS(cls::Super), \
|
|
sizeof(cls), \
|
|
ptrs, \
|
|
create }; \
|
|
_DECLARE_TI(cls)
|
|
|
|
#define _IMP_CREATE_OBJ(cls) \
|
|
void cls::InPlaceConstructor(void *mem) { new((EInPlace *)mem) cls; }
|
|
|
|
#define IMPLEMENT_POINTY_CLASS(cls) \
|
|
_IMP_CREATE_OBJ(cls) \
|
|
_IMP_PCLASS(cls,cls::PointerOffsets,cls::InPlaceConstructor) \
|
|
const size_t cls::PointerOffsets[] = {
|
|
|
|
#define IMPLEMENT_CLASS(cls) \
|
|
_IMP_CREATE_OBJ(cls) \
|
|
_IMP_PCLASS(cls,NULL,cls::InPlaceConstructor)
|
|
|
|
#define IMPLEMENT_ABSTRACT_CLASS(cls) \
|
|
_IMP_PCLASS(cls,NULL,NULL)
|
|
|
|
enum EObjectFlags
|
|
{
|
|
// GC flags
|
|
OF_White0 = 1 << 0, // Object is white (type 0)
|
|
OF_White1 = 1 << 1, // Object is white (type 1)
|
|
OF_Black = 1 << 2, // Object is black
|
|
OF_Fixed = 1 << 3, // Object is fixed (should not be collected)
|
|
OF_Rooted = 1 << 4, // Object is soft-rooted
|
|
OF_EuthanizeMe = 1 << 5, // Object wants to die
|
|
OF_Cleanup = 1 << 6, // Object is now being deleted by the collector
|
|
OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning
|
|
|
|
OF_WhiteBits = OF_White0 | OF_White1,
|
|
OF_MarkBits = OF_WhiteBits | OF_Black,
|
|
|
|
// Other flags
|
|
OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
|
|
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
|
|
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
|
|
};
|
|
|
|
template<class T> class TObjPtr;
|
|
|
|
namespace GC
|
|
{
|
|
enum EGCState
|
|
{
|
|
GCS_Pause,
|
|
GCS_Propagate,
|
|
GCS_Sweep,
|
|
GCS_Finalize
|
|
};
|
|
|
|
// Number of bytes currently allocated through M_Malloc/M_Realloc.
|
|
extern size_t AllocBytes;
|
|
|
|
// Amount of memory to allocate before triggering a collection.
|
|
extern size_t Threshold;
|
|
|
|
// List of gray objects.
|
|
extern DObject *Gray;
|
|
|
|
// List of every object.
|
|
extern DObject *Root;
|
|
|
|
// Current white value for potentially-live objects.
|
|
extern uint32 CurrentWhite;
|
|
|
|
// Current collector state.
|
|
extern EGCState State;
|
|
|
|
// Position of GC sweep in the list of objects.
|
|
extern DObject **SweepPos;
|
|
|
|
// Size of GC pause.
|
|
extern int Pause;
|
|
|
|
// Size of GC steps.
|
|
extern int StepMul;
|
|
|
|
// Current white value for known-dead objects.
|
|
static inline uint32 OtherWhite()
|
|
{
|
|
return CurrentWhite ^ OF_WhiteBits;
|
|
}
|
|
|
|
// Frees all objects, whether they're dead or not.
|
|
void FreeAll();
|
|
|
|
// Does one collection step.
|
|
void Step();
|
|
|
|
// Does a complete collection.
|
|
void FullGC();
|
|
|
|
// Handles the grunt work for a write barrier.
|
|
void Barrier(DObject *pointing, DObject *pointed);
|
|
|
|
// Handles a write barrier.
|
|
static inline void WriteBarrier(DObject *pointing, DObject *pointed);
|
|
|
|
// Handles a write barrier for a pointer that isn't inside an object.
|
|
static inline void WriteBarrier(DObject *pointed);
|
|
|
|
// Handles a read barrier.
|
|
template<class T> inline T *ReadBarrier(T *&obj)
|
|
{
|
|
if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe))
|
|
{
|
|
return obj;
|
|
}
|
|
return obj = NULL;
|
|
}
|
|
|
|
// Check if it's time to collect, and do a collection step if it is.
|
|
static inline void CheckGC()
|
|
{
|
|
if (AllocBytes >= Threshold)
|
|
Step();
|
|
}
|
|
|
|
// Forces a collection to start now.
|
|
static inline void StartCollection()
|
|
{
|
|
Threshold = AllocBytes;
|
|
}
|
|
|
|
// Marks a white object gray. If the object wants to die, the pointer
|
|
// is NULLed instead.
|
|
void Mark(DObject **obj);
|
|
|
|
// For cleanup
|
|
void DelSoftRootHead();
|
|
|
|
// Soft-roots an object.
|
|
void AddSoftRoot(DObject *obj);
|
|
|
|
// Unroots an object.
|
|
void DelSoftRoot(DObject *obj);
|
|
|
|
template<class T> void Mark(T *&obj)
|
|
{
|
|
union
|
|
{
|
|
T *t;
|
|
DObject *o;
|
|
};
|
|
o = obj;
|
|
Mark(&o);
|
|
obj = t;
|
|
}
|
|
template<class T> void Mark(TObjPtr<T> &obj);
|
|
}
|
|
|
|
// A template class to help with handling read barriers. It does not
|
|
// handle write barriers, because those can be handled more efficiently
|
|
// with knowledge of the object that holds the pointer.
|
|
template<class T>
|
|
class TObjPtr
|
|
{
|
|
union
|
|
{
|
|
T *p;
|
|
DObject *o;
|
|
};
|
|
public:
|
|
TObjPtr() throw()
|
|
{
|
|
}
|
|
TObjPtr(T *q) throw()
|
|
: p(q)
|
|
{
|
|
}
|
|
TObjPtr(const TObjPtr<T> &q) throw()
|
|
: p(q.p)
|
|
{
|
|
}
|
|
T *operator=(T *q) throw()
|
|
{
|
|
return p = q;
|
|
// The caller must now perform a write barrier.
|
|
}
|
|
operator T*() throw()
|
|
{
|
|
return GC::ReadBarrier(p);
|
|
}
|
|
T &operator*()
|
|
{
|
|
T *q = GC::ReadBarrier(p);
|
|
assert(q != NULL);
|
|
return *q;
|
|
}
|
|
T **operator&() throw()
|
|
{
|
|
// Does not perform a read barrier. The only real use for this is with
|
|
// the DECLARE_POINTER macro, where a read barrier would be a very bad
|
|
// thing.
|
|
return &p;
|
|
}
|
|
T *operator->() throw()
|
|
{
|
|
return GC::ReadBarrier(p);
|
|
}
|
|
bool operator<(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) < u;
|
|
}
|
|
bool operator<=(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) <= u;
|
|
}
|
|
bool operator>(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) > u;
|
|
}
|
|
bool operator>=(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) >= u;
|
|
}
|
|
bool operator!=(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) != u;
|
|
}
|
|
bool operator==(T *u) throw()
|
|
{
|
|
return GC::ReadBarrier(p) == u;
|
|
}
|
|
|
|
template<class U> friend inline FArchive &operator<<(FArchive &arc, TObjPtr<U> &o);
|
|
template<class U> friend inline void GC::Mark(TObjPtr<U> &obj);
|
|
friend class DObject;
|
|
};
|
|
|
|
template<class T> inline FArchive &operator<<(FArchive &arc, TObjPtr<T> &o)
|
|
{
|
|
return arc << o.p;
|
|
}
|
|
|
|
// Use barrier_cast instead of static_cast when you need to cast
|
|
// the contents of a TObjPtr to a related type.
|
|
template<class T,class U> inline T barrier_cast(TObjPtr<U> &o)
|
|
{
|
|
return static_cast<T>(static_cast<U *>(o));
|
|
}
|
|
|
|
template<class T> inline void GC::Mark(TObjPtr<T> &obj)
|
|
{
|
|
GC::Mark(&obj.o);
|
|
}
|
|
|
|
class DObject
|
|
{
|
|
public:
|
|
static PClass _StaticType;
|
|
virtual PClass *StaticType() const { return &_StaticType; }
|
|
static ClassReg RegistrationInfo, *RegistrationInfoPtr;
|
|
static void InPlaceConstructor (void *mem);
|
|
private:
|
|
typedef DObject ThisClass;
|
|
|
|
// Per-instance variables. There are four.
|
|
private:
|
|
PClass *Class; // This object's type
|
|
public:
|
|
DObject *ObjNext; // Keep track of all allocated objects
|
|
DObject *GCNext; // Next object in this collection list
|
|
uint32 ObjectFlags; // Flags for this object
|
|
|
|
public:
|
|
DObject ();
|
|
DObject (PClass *inClass);
|
|
virtual ~DObject ();
|
|
|
|
inline bool IsKindOf (const PClass *base) const;
|
|
inline bool IsA (const PClass *type) const;
|
|
|
|
void SerializeUserVars(FArchive &arc);
|
|
virtual void Serialize (FArchive &arc);
|
|
|
|
// For catching Serialize functions in derived classes
|
|
// that don't call their base class.
|
|
void CheckIfSerialized () const;
|
|
|
|
virtual void Destroy ();
|
|
|
|
// If you need to replace one object with another and want to
|
|
// change any pointers from the old object to the new object,
|
|
// use this method.
|
|
virtual size_t PointerSubstitution (DObject *old, DObject *notOld);
|
|
static size_t StaticPointerSubstitution (DObject *old, DObject *notOld);
|
|
|
|
PClass *GetClass() const
|
|
{
|
|
if (Class == NULL)
|
|
{
|
|
// Save a little time the next time somebody wants this object's type
|
|
// by recording it now.
|
|
const_cast<DObject *>(this)->Class = StaticType();
|
|
}
|
|
return Class;
|
|
}
|
|
|
|
void SetClass (PClass *inClass)
|
|
{
|
|
Class = inClass;
|
|
}
|
|
|
|
void *operator new(size_t len)
|
|
{
|
|
return M_Malloc(len);
|
|
}
|
|
|
|
void operator delete (void *mem)
|
|
{
|
|
M_Free(mem);
|
|
}
|
|
|
|
// GC fiddling
|
|
|
|
// An object is white if either white bit is set.
|
|
bool IsWhite() const
|
|
{
|
|
return !!(ObjectFlags & OF_WhiteBits);
|
|
}
|
|
|
|
bool IsBlack() const
|
|
{
|
|
return !!(ObjectFlags & OF_Black);
|
|
}
|
|
|
|
// An object is gray if it isn't white or black.
|
|
bool IsGray() const
|
|
{
|
|
return !(ObjectFlags & OF_MarkBits);
|
|
}
|
|
|
|
// An object is dead if it's the other white.
|
|
bool IsDead() const
|
|
{
|
|
return !!(ObjectFlags & GC::OtherWhite() & OF_WhiteBits);
|
|
}
|
|
|
|
void ChangeWhite()
|
|
{
|
|
ObjectFlags ^= OF_WhiteBits;
|
|
}
|
|
|
|
void MakeWhite()
|
|
{
|
|
ObjectFlags = (ObjectFlags & ~OF_MarkBits) | (GC::CurrentWhite & OF_WhiteBits);
|
|
}
|
|
|
|
void White2Gray()
|
|
{
|
|
ObjectFlags &= ~OF_WhiteBits;
|
|
}
|
|
|
|
void Black2Gray()
|
|
{
|
|
ObjectFlags &= ~OF_Black;
|
|
}
|
|
|
|
void Gray2Black()
|
|
{
|
|
ObjectFlags |= OF_Black;
|
|
}
|
|
|
|
// Marks all objects pointed to by this one. Returns the (approximate)
|
|
// amount of memory used by this object.
|
|
virtual size_t PropagateMark();
|
|
|
|
protected:
|
|
// This form of placement new and delete is for use *only* by PClass's
|
|
// CreateNew() method. Do not use them for some other purpose.
|
|
void *operator new(size_t, EInPlace *mem)
|
|
{
|
|
return (void *)mem;
|
|
}
|
|
|
|
void operator delete (void *mem, EInPlace *)
|
|
{
|
|
M_Free (mem);
|
|
}
|
|
};
|
|
|
|
static inline void GC::WriteBarrier(DObject *pointing, DObject *pointed)
|
|
{
|
|
if (pointed != NULL && pointed->IsWhite() && pointing->IsBlack())
|
|
{
|
|
Barrier(pointing, pointed);
|
|
}
|
|
}
|
|
|
|
static inline void GC::WriteBarrier(DObject *pointed)
|
|
{
|
|
if (pointed != NULL && State == GCS_Propagate && pointed->IsWhite())
|
|
{
|
|
Barrier(NULL, pointed);
|
|
}
|
|
}
|
|
|
|
#include "dobjtype.h"
|
|
|
|
inline bool DObject::IsKindOf (const PClass *base) const
|
|
{
|
|
return base->IsAncestorOf (GetClass ());
|
|
}
|
|
|
|
inline bool DObject::IsA (const PClass *type) const
|
|
{
|
|
return (type == GetClass());
|
|
}
|
|
|
|
#endif //__DOBJECT_H__
|