cChunk now tracks its neighbors as direct pointers; used in UnboundedRelGetBlock et al.
git-svn-id: http://mc-server.googlecode.com/svn/trunk@1073 0a769ca7-a7f5-676a-18bf-c427514a06d6master
parent
15fe5a1d41
commit
310c1076a7
120
source/Chunk.cpp
120
source/Chunk.cpp
|
@ -55,7 +55,11 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc
|
|||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cChunk:
|
||||
|
||||
cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World)
|
||||
cChunk::cChunk(
|
||||
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
|
||||
cChunkMap * a_ChunkMap, cWorld * a_World,
|
||||
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP
|
||||
)
|
||||
: m_PosX( a_ChunkX )
|
||||
, m_PosY( a_ChunkY )
|
||||
, m_PosZ( a_ChunkZ )
|
||||
|
@ -69,8 +73,27 @@ cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap,
|
|||
, m_IsDirty(false)
|
||||
, m_IsSaving(false)
|
||||
, m_StayCount(0)
|
||||
, m_NeighborXM(a_NeighborXM)
|
||||
, m_NeighborXP(a_NeighborXP)
|
||||
, m_NeighborZM(a_NeighborZM)
|
||||
, m_NeighborZP(a_NeighborZP)
|
||||
{
|
||||
// LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId());
|
||||
if (a_NeighborXM != NULL)
|
||||
{
|
||||
a_NeighborXM->m_NeighborXP = this;
|
||||
}
|
||||
if (a_NeighborXP != NULL)
|
||||
{
|
||||
a_NeighborXP->m_NeighborXM = this;
|
||||
}
|
||||
if (a_NeighborZM != NULL)
|
||||
{
|
||||
a_NeighborZM->m_NeighborZP = this;
|
||||
}
|
||||
if (a_NeighborZP != NULL)
|
||||
{
|
||||
a_NeighborZP->m_NeighborZM = this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,6 +125,23 @@ cChunk::~cChunk()
|
|||
(*itr)->Destroy();
|
||||
}
|
||||
m_Entities.clear();
|
||||
|
||||
if (m_NeighborXM != NULL)
|
||||
{
|
||||
m_NeighborXM->m_NeighborXP = NULL;
|
||||
}
|
||||
if (m_NeighborXP != NULL)
|
||||
{
|
||||
m_NeighborXP->m_NeighborXM = NULL;
|
||||
}
|
||||
if (m_NeighborZM != NULL)
|
||||
{
|
||||
m_NeighborZM->m_NeighborZP = NULL;
|
||||
}
|
||||
if (m_NeighborZP != NULL)
|
||||
{
|
||||
m_NeighborZP->m_NeighborZM = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -772,9 +812,11 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
|
|||
{
|
||||
if ((a_RelY < 0) || (a_RelY > cChunkDef::Height))
|
||||
{
|
||||
LOGWARNING("UnboundedRelGetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it in this chunk?
|
||||
if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
|
||||
{
|
||||
int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
|
@ -782,6 +824,26 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
|
|||
a_BlockMeta = GetMeta(BlockIdx);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not in this chunk, try walking the neighbors first:
|
||||
if ((a_RelX < 0) && (m_NeighborXM != NULL))
|
||||
{
|
||||
return m_NeighborXM->UnboundedRelGetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
|
||||
{
|
||||
return m_NeighborXP->UnboundedRelGetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ < 0) && (m_NeighborZM != NULL))
|
||||
{
|
||||
return m_NeighborZM->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
|
||||
{
|
||||
return m_NeighborZP->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
// Neighbors not available, use the chunkmap to locate the chunk:
|
||||
return m_ChunkMap->LockedGetBlock(
|
||||
m_PosX * cChunkDef::Width + a_RelX,
|
||||
ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
|
||||
|
@ -796,11 +858,38 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
|
|||
|
||||
bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
if ((a_RelY < 0) || (a_RelY > cChunkDef::Height))
|
||||
{
|
||||
LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it in this chunk?
|
||||
if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
|
||||
{
|
||||
SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not in this chunk, try walking the neighbors first:
|
||||
if ((a_RelX < 0) && (m_NeighborXM != NULL))
|
||||
{
|
||||
return m_NeighborXM->UnboundedRelSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
|
||||
{
|
||||
return m_NeighborXP->UnboundedRelSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ < 0) && (m_NeighborZM != NULL))
|
||||
{
|
||||
return m_NeighborZM->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
|
||||
{
|
||||
return m_NeighborZP->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
// Neighbors not available, use the chunkmap to locate the chunk:
|
||||
return m_ChunkMap->LockedSetBlock(
|
||||
m_PosX * cChunkDef::Width + a_RelX,
|
||||
ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
|
||||
|
@ -815,11 +904,38 @@ bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE
|
|||
|
||||
bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
if ((a_RelY < 0) || (a_RelY > cChunkDef::Height))
|
||||
{
|
||||
LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it in this chunk?
|
||||
if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width))
|
||||
{
|
||||
FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not in this chunk, try walking the neighbors first:
|
||||
if ((a_RelX < 0) && (m_NeighborXM != NULL))
|
||||
{
|
||||
return m_NeighborXM->UnboundedRelFastSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL))
|
||||
{
|
||||
return m_NeighborXP->UnboundedRelFastSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ < 0) && (m_NeighborZM != NULL))
|
||||
{
|
||||
return m_NeighborZM->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL))
|
||||
{
|
||||
return m_NeighborZP->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
// Neighbors not available, use the chunkmap to locate the chunk:
|
||||
return m_ChunkMap->LockedFastSetBlock(
|
||||
m_PosX * cChunkDef::Width + a_RelX,
|
||||
ZERO_CHUNK_Y * cChunkDef::Height + a_RelY,
|
||||
|
|
|
@ -59,7 +59,11 @@ class cChunk :
|
|||
public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef
|
||||
{
|
||||
public:
|
||||
cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World);
|
||||
cChunk(
|
||||
int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords
|
||||
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
|
||||
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks
|
||||
);
|
||||
~cChunk();
|
||||
|
||||
bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
|
||||
|
@ -267,6 +271,11 @@ private:
|
|||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
|
||||
int m_BlockTickX, m_BlockTickY, m_BlockTickZ;
|
||||
|
||||
cChunk * m_NeighborXM; // Neighbor at [X - 1, Z]
|
||||
cChunk * m_NeighborXP; // Neighbor at [X + 1, Z]
|
||||
cChunk * m_NeighborZM; // Neighbor at [X, Z - 1]
|
||||
cChunk * m_NeighborZP; // Neighbor at [X, Z + 1]
|
||||
|
||||
void RemoveBlockEntity( cBlockEntity* a_BlockEntity );
|
||||
void AddBlockEntity( cBlockEntity* a_BlockEntity );
|
||||
|
@ -303,13 +312,13 @@ private:
|
|||
/// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log)
|
||||
bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
/// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
/// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
|
||||
|
||||
/// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
/// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
/// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick()
|
||||
bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
};
|
||||
|
||||
|
|
|
@ -84,7 +84,38 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ)
|
|||
|
||||
|
||||
|
||||
cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ )
|
||||
cChunkMap::cChunkLayer * cChunkMap::FindLayerForChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE)));
|
||||
const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE)));
|
||||
return FindLayer(LayerX, LayerZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkMap::cChunkLayer * cChunkMap::FindLayer(int a_LayerX, int a_LayerZ)
|
||||
{
|
||||
ASSERT(m_CSLayers.IsLockedByCurrentThread());
|
||||
|
||||
for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
|
||||
{
|
||||
if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ))
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
} // for itr - m_Layers[]
|
||||
|
||||
// Not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE)));
|
||||
const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE)));
|
||||
|
@ -98,6 +129,8 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ
|
|||
cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
|
||||
{
|
||||
// No need to lock m_CSLayers, since it's already locked by the operation that called us
|
||||
ASSERT(m_CSLayers.IsLockedByCurrentThread());
|
||||
|
||||
cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ );
|
||||
if (Layer == NULL)
|
||||
{
|
||||
|
@ -224,6 +257,22 @@ bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLO
|
|||
|
||||
|
||||
|
||||
cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
ASSERT(m_CSLayers.IsLockedByCurrentThread());
|
||||
|
||||
cChunkLayer * Layer = FindLayerForChunk(a_ChunkX, a_ChunkZ);
|
||||
if (Layer == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return Layer->FindChunk(a_ChunkX, a_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude)
|
||||
{
|
||||
cCSLock Lock(m_CSLayers);
|
||||
|
@ -1786,7 +1835,11 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch
|
|||
int Index = LocalX + LocalZ * LAYER_SIZE;
|
||||
if (m_Chunks[Index] == NULL)
|
||||
{
|
||||
m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld());
|
||||
cChunk * neixm = (LocalX > 0) ? m_Chunks[Index - 1] : m_Parent->FindChunk(a_ChunkX - 1, a_ChunkZ);
|
||||
cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ);
|
||||
cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1);
|
||||
cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1);
|
||||
m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp);
|
||||
}
|
||||
return m_Chunks[Index];
|
||||
}
|
||||
|
@ -1795,6 +1848,25 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch
|
|||
|
||||
|
||||
|
||||
cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE;
|
||||
const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE;
|
||||
|
||||
if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1)))
|
||||
{
|
||||
ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Index = LocalX + LocalZ * LAYER_SIZE;
|
||||
return m_Chunks[Index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
|
||||
|
|
|
@ -264,6 +264,9 @@ private:
|
|||
/// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
|
||||
cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ );
|
||||
|
||||
/// Returns the specified chunk, or NULL if not created yet
|
||||
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
int GetX(void) const {return m_LayerX; }
|
||||
int GetZ(void) const {return m_LayerZ; }
|
||||
|
||||
|
@ -288,12 +291,20 @@ private:
|
|||
};
|
||||
|
||||
typedef std::list<cChunkLayer *> cChunkLayerList;
|
||||
// TODO: Use smart pointers for cChunkLayerList as well, so that ticking and saving needn't lock the entire layerlist
|
||||
// This however means that cChunkLayer needs to interlock its m_Chunks[]
|
||||
|
||||
cChunkLayer * GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); // Creates the layer if it doesn't already exist
|
||||
cChunkLayer * GetLayer( int a_LayerX, int a_LayerZ ); // Creates the layer if it doesn't already exist
|
||||
void RemoveLayer( cChunkLayer* a_Layer );
|
||||
/// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked.
|
||||
cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
/// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked.
|
||||
cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ);
|
||||
|
||||
/// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found.
|
||||
cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ);
|
||||
|
||||
/// Returns the specified cChunkLayer object; creates it if not found.
|
||||
cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ);
|
||||
|
||||
void RemoveLayer(cChunkLayer * a_Layer);
|
||||
|
||||
cCriticalSection m_CSLayers;
|
||||
cChunkLayerList m_Layers;
|
||||
|
@ -313,6 +324,9 @@ private:
|
|||
|
||||
/// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
|
||||
bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap.
|
||||
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue