Full Code of zllangct/ecs for AI

master ac8917a3ed6e cached
85 files
208.8 KB
70.2k tokens
942 symbols
1 requests
Download .txt
Showing preview only (246K chars total). Download the full file or copy to clipboard to get everything.
Repository: zllangct/ecs
Branch: master
Commit: ac8917a3ed6e
Files: 85
Total size: 208.8 KB

Directory structure:
gitextract_p66ljwpu/

├── .gitignore
├── LICENSE
├── README.md
├── chunk.go
├── collection_iter.go
├── component.go
├── component_collection.go
├── component_getter.go
├── component_meta.go
├── component_meta_test.go
├── component_operate_task.go
├── component_set.go
├── component_set_test.go
├── component_test.go
├── component_utils.go
├── compound.go
├── compound_test.go
├── compound_utils.go
├── concurrent_map.go
├── concurrent_map_test.go
├── direct_api.go
├── entity.go
├── entity_info.go
├── entity_set.go
├── entity_test.go
├── example/
│   ├── benchmark-0/
│   │   ├── components.go
│   │   ├── damage_system.go
│   │   ├── game_common.go
│   │   ├── game_ecs.go
│   │   ├── game_normal.go
│   │   ├── go.mod
│   │   ├── main_benchmark_test.go
│   │   ├── main_test.go
│   │   ├── move_system.go
│   │   └── simu_load_system.go
│   └── fake-simple-game-server/
│       ├── client/
│       │   └── fake_client.go
│       ├── game/
│       │   ├── chat.go
│       │   ├── empty_system.go
│       │   ├── fake_game.go
│       │   ├── move_component.go
│       │   ├── move_system.go
│       │   ├── player_component.go
│       │   ├── position_sync_system.go
│       │   └── session.go
│       ├── gm/
│       │   └── gm.go
│       ├── go.mod
│       ├── main.go
│       └── network/
│           └── fake_net.go
├── fixed_string.go
├── fixed_string_test.go
├── fixed_string_utils.go
├── go.mod
├── go.sum
├── goroutine_id.go
├── goroutine_pool.go
├── goroutine_pool_benchmark_test.go
├── internal_type_mock.go
├── logger.go
├── metrics.go
├── optimizer.go
├── optimizer_benchmark_test.go
├── ordered_int_set.go
├── ordered_int_set_test.go
├── serialize.go
├── shape_getter.go
├── shape_getter_test.go
├── shape_iter.go
├── sparse_array.go
├── system.go
├── system_event.go
├── system_flow.go
├── system_group.go
├── system_group_test.go
├── system_requirement.go
├── unordered_collection.go
├── unordered_collection_test.go
├── unordered_collection_with_id.go
├── unordered_collection_with_id_test.go
├── utility.go
├── utils.go
├── utils_test.go
├── world.go
├── world_async.go
├── world_sync.go
└── world_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea/


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2023, zllangct

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. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.


================================================
FILE: README.md
================================================
# ECS
这是一个ECS(Entity-Component-System)Go语言版本的实现,它聚焦于游戏领域的应用,帮助你快速构建一个高内聚、低耦合、易扩展、高性能的并行化游戏世界。
## Contents
* [快速开始](#快速开始)
    + [安装](#安装)
    + [简单示例](#简单示例)
* [快速了解](#快速了解)
    + [什么是ECS?](#什么是ECS?)
    + [游戏领域为什么需要ECS?](#游戏领域为什么需要ECS?)
    + [基本架构](#基本架构)
    + [基本概念](#基本概念)
        - [World](#World)
        - [Entity](#Entity)
        - [Component](#Component)
        - [System](#System)
        - [Utility](#Utility)
        - [Shape](#Shape)
        - [FixedString](#FixedString)
        - [“下一帧生效"](#“下一帧生效")
    + [设计思路](#设计思路)
* [使用教程](#使用教程)
    + [创建一个World](#创建一个World)
        - [SyncWorld](#SyncWorld)
        - [AsyncWorld](#AsyncWorld)
    - [创建一个Entity](#创建一个Entity)
    - [创建一个Component](#创建一个Component)
    - [创建一个System](#创建一个System)
        * [阶段事件](#阶段事件)
        * [阶段特征](#阶段特征)
    + [使用Utility与System交互](#使用Utility与System交互)
    + [ECS与外围系统交互](#ECS与外围系统交互)
        - [SyncWorld](#SyncWorld)
        - [AsyncWorld](#AsyncWorld)
    + [如何处理System的执行顺序](#如何处理System的执行顺序)
    + [如何选择不同类型的Component](#如何选择不同类型的Component)
        - [常规组件](#常规组件)
        - [Free组件](#Free组件)
        - [Disposable组件](#Disposable组件)
        - [FreeDisposable组件](#FreeDisposable组件)
    + [系统中获取组件的方式](#系统中获取组件的方式)
    + [系统间的数据流动](#系统间的数据流动)
    + [一个完整的例子](#一个完整的例子)
* [Benchmark](#Benchmark)
    + [测试用的代码](#测试用的代码)
    + [测试结果](#测试结果)
    + [结论](#结论)
* [API文档](#API文档)
* [设计细节](#设计细节)
    + [架构](#架构)
    + [日志](#日志)
    + [World](#World)
    + [Entity](#Entity)
    + [Component](#Component)
    + [System](#System)
    + [Utility](#Utility)
    + [Shape](#Shape)
    + [Compound](#Compound)
    + [一次Update的执行流程](#一次Update的执行流程)
    + [从添加Component到生效](#从添加Component到生效)
    + [容器](#容器)
    + [迭代器](#迭代器)
    + [协程池](#协程池)
    + [ECS序列化和反序列化](#ECS序列化和反序列化)
    + [ECS中的并行](#ECS中的并行)
    + [EntityID的管理](#EntityID的管理)
    + [如何行为限制](#如何行为限制)
    + [优化器](#优化器)
    + [统计器](#统计器)
* [特别注意](#特别注意)
* [存在的一些问题](#存在的一些问题)
* [写在最后](#写在最后)
* [TODO](#TODO)
## 快速开始
### 安装
```shell
go install github.com/zllangct/ecs
```
### 简单示例
这是一个不完整的示例,但可以帮助您快速了解如何在你的程序中接入和使用ecs框架。
```go
package main

import (
    "fmt"
    "github.com/zllangct/ecs"
    "time"
)

// 定义你的系统, 需要嵌套ecs.System[T],T为你的System类型
type TestSystem struct {
	ecs.System[TestSystem]
}

// 系统Init事件
func (w *TestSystem) Init(si SystemInitializer) {
	// 申明系统感兴趣的组件, 系统内无法获取未申明的组件
	w.SetRequirements(si, &TestComponent1{}, &TestComponent2{}, &TestComponent3{})
}

func (w *TestSystem) Update(event Event) {
	// 获取系统感兴趣的组件, 作为遍历的索引,也可以叫做key组件
	iter := ecs.GetComponentAll[TestComponent1](w)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		// 获取关联组件,他们和key组件属于同一实体,也有人称之为"兄弟组件"
		c2 := ecs.GetRelated[TestComponent2](w, c.owner)
		if c2 == nil {
			continue
		}
		
		// 一些简单的处理逻辑
		for i := 0; i < testOptimizerDummyMaxFor; i++ {
			c.Field1 += i
		}

		for i := 0; i < testOptimizerDummyMaxFor; i++ {
			c2.Field2 += i
		}
	}
}

func main() {
	// 创建一个世界需要的配置
	config := ecs.NewDefaultWorldConfig()
	// 创建一个世界
	world := ecs.NewSyncWorld(config)
	// 注册系统
	ecs.RegisterSystem[TestSystem](world)

	// 启动你的世界
	world.Startup()

	// 为你的世界添加实体
	entities := make([]Entity, count)
	for i := 0; i < count; i++ {
		e1 := world.NewEntity()
		world.Add(e1, &TestComponent1{}, &TestComponent2{}, &TestComponent3{})
		entities[i] = e1
	}

	// 持续更新你的世界
	for {
		world.Update()
		time.Sleep(time.Second)
	}
}
```
示例中的 ```__world_Test_S_1``` ```__world_Test_C_1```为系统和组件,后面会详细介绍,完整的代码请移步 [ world_test.go ](./world_test.go)。
## 快速了解
### 什么是ECS?
ECS是Entity-Component-System的缩写,它是一种数据驱动的架构,它将数据和逻辑分离开来,使得数据和逻辑可以独立的扩展和复用。   
* 实体:实体代表通用对象。例如,在游戏引擎上下文中,每个粗制游戏对象都表示为实体。通常,它仅由一个唯一的ID组成。实现通常为此使用普通整数。  
* 组件:组件将实体标记为具有特定方面的标签,并保留建模该方面所需的数据。例如,每个可能造成伤害的游戏对象都可能具有与其实体相关的健康组件。实现通常使用结构,类或关联阵列。  
* 系统:系统是一个过程,可用于所有具有所需组件的实体。 例如,物理系统可能会查询具有质量,速度和位置成分的实体,并迭代对每个实体的组件集进行物理计算的结果。 

实体的行为可以在运行时由添加、删除或修改组件的系统更改。 这消除了在面向对象编程技术中难以理解、维护和扩展的深层和广泛继承层次结构的模糊问题。
常见的ECS方法与面向数据的设计技术高度兼容,并经常与之结合。 一个组件的所有实例的数据通常一起存储在物理内存中,这使得在多个实体上操作的系统能够有效地访问内存。  
(来源:[维基百科](https://en.wikipedia.org/wiki/Entity_component_system))
### 游戏领域为什么需要ECS?
总结起来大概是两个词:”瓶颈“和”契合“。  
&emsp;&emsp;此处我们忽略一些ECS的其他特性,比如解耦、易扩展等,在其他设计模式下也能够实现,这不是ECS的显著特点和优势。同时也应该提到,ECS并不是万能的灵丹妙药,
并不能解决通用编程的所有问题,作为一种设计思路,能够很好的解决一些特定的问题,就已经足以促使我们学习和使用它。谈到“瓶颈”,首先我们来看看游戏领域遇到的一些问题,当然这不是全部,我们重点关注与ECS
相关的问题。
* 游戏属于密集型运算类应用,无论是客户端还是游戏后台都需要大量的运算,比如物理、AI、视野等相关的计算。
* 游戏对时延的要求非常高,比如FPS游戏,每一帧的渲染时间都是有限的,如果超过了这个时间,就会出现卡顿的现象。我们一直在致力于创造更低时延、更流畅的游戏体验,但是随着游戏的复杂度越来越高,计算量越来越大,
这个问题也越来越严重。虽然现在硬件资源日益强大,可以满足大部分的需求,同事可以通过分布式的计算来解决一些问题,但分布式带来的网络消耗也是非常大的,调用链路复杂后,问题更明显,分布式可以解决大计算量的问题
,但延时问题并不能有效解决。为了低延时,我们甚至常常使用“直连”的方式处理客户端和服务器之间的通讯,目的仅仅是为了rpc的消耗。除了更优秀的硬件和分布式,我们能做的还有多线程,多线程可以有效的利用多核CPU的资源,
,并通过内存进行线程间的交互,非常高效,避免了分布式中rpc的网络消耗。但是多线程会使得代码变得复杂,而且线程间的交互也会带来一些问题,比如线程安全、死锁等,甚至由于数据竞争的存在,很容易写出比单线程更低效的
多线程游戏系统。游戏系统对时序的要求非常高,多线程的异步逻辑往往很难保证正确的逻辑时序,所以通常我们的游戏服务更多的是采用单线程模式,仅仅会把耗时、逻辑独立的部分逻辑使用多线程优化,主体维持单线程。于是不能
愉快的利用分布式资源和多核资源成为了游戏领域的瓶颈。
* 游戏系统的更新,大家很熟悉的就是热更新,往往采用内嵌脚本语言的方式去实现,但代价就是性能和动态语言的缺点(同时也算是优点,矛盾总是存在的)。还有一些使用共享内存方案,将游戏系统的状态数据存放在共享内存中,
重启更新游戏后台程序后从共享内存中读取并恢复游戏状态,但对于OOP的设计模式来说,这将是一个非常复杂的过程,可能你需要一个自定义的内存分配器,因为只有这样你才能处理好共享内存的分配。可能你需要定制化你所使用
到的容器,可能需要处理恢复虚表等令人头痛的问题。还有一些序列化的方案,将游戏状态在更新前序列化并保存,更新后再反序列化并恢复状态,和共享内存方案相比,这解决了一些头疼的问题,让状态的存储和恢复变得简单了一些
,但是序列化的过程也是一个非常耗时的过程,而且序列化的数据量也是非常大的,这也是一个非常大的问题。所以游戏系统的更新也是一个非常头疼的问题。
* 在大世界的游戏世界中,单一的游戏服务器往往不能满足游戏的需求,无论用怎么样的构架方式,都避免不了游戏数据在服务器之前的迁移,不同的数据分布在不同的对象中,转移中数据的处理将是一个复杂的过程,同时也往往
会在这个环节产生很多的BUG,让你的同事和你非常头疼。  

&emsp;&emsp;刻意提到上面的一些问题,是因为我们可以通过ECS的方式去改善或者解决这些问题。上面的这些“瓶颈”我们总结一下,可以归纳为以下几点:1、怎么利用多核资源 2、怎么做到优雅更新 
3、怎么做到易于数据迁移,快速的序列化状态数据 4、怎么利用分布式资源。非ECS的方案也能通过一定的方法来解决上诉问题,但我们希望一种更简单,代价更低,甚至是更好的解决办法,我们可以逐个的分析,尝试着用ECS的方式去解决这些问题。
针对多核并行这一点,我们可以先看看ECS的特点,其中我们注意到,Component中仅有数据,一个对象的抽象,将会是拆分成不同的“数据块”分布在不同的Component中,数据是离散的,数据是边界明显的,单个System中仅是处理
改系统感兴趣Component,由此可见,数据是根据System的需求组合在一起的,组合后的数据成为System逻辑意义上的“对象”,Component是有关联的最小集合,为组合提供了更多的可能性,更灵活的复用。那么Component上的明显隔离,
System的互相独立这两点为并行创造了有利条件,因为独立的系统之间不会有数据竞争,当然System之间可能会对同一Component感兴趣,这个问题很好解决,只需要计算出系统间的组件依赖,就可以知道那些系统是可以并行的,
而这一过程可以在ECS的启动阶段完成计算,不会影响到运行时的性能。 然后利用一定的算法,系统拆分成互不依赖多批次,然后再分批次并行执行。并行的过程中由于没有数据竞争,我们完全不需要使用锁的方式去保证线程安全,
因为它们天生就是安全的,无锁化的并行将给并行带来巨大的性能提升,永远不会把大量的时间消耗在锁的竞争上,不会导致多线程比单线程性能更低的结果,所以ECS的设计模式就是天生为并行而生的。 第二点,怎么优雅的更新,上面也提到了
一些更新方案会因为系统状态的复杂性导致很难做到全量状态的存储,并恢复到更新前的状态。ECS中所有的状态即Component都是单独存储的,并且结构异常简单,是通过线性数组存储,数组是内存连续的容器,很容易做到全量的存储,所以
ECS中仅需要简单的储存所有Component的容器,这个过程非常简单,你可以直接通过二进制的形式转存到文件或者共享内存中,不需要序列化,这个过程非常高效。在恢复上,你可以简单的将二进制转换为原有的Component数组,然后ECS
系统中的所有状态就恢复了,下一次Update时,系统已经恢复如初。正是这个特性,同样适合解决第三点中提到的问题,数据分离且易于转存的特性使得数据在不同的服务器间的转移逻辑异常简单。第四点,ECS的系统是独立的,数据是界限明显的,
你可以轻易的找到独立的System和Component的组合,并将他们拆分至其他节点去运行。  
&emsp;&emsp;接下来再谈谈“契合”,ECS的数据是离散的,是有明显的边界,那么在开发中涉及到的应用中都贴合这个特性么?显然不是的,比如一套复杂的ERP管理系统,每一个API都需要访问到大量的数据,
API指间需要访问的数据间有大量的耦合,我们根本无法很好的拆分出功能独立的Component,显然这跟ECS不是那么的契合。游戏世界中往往有大量的游戏对象,并且需要高频的创建或者销毁,这在ECS中是非常廉价的操作,因为
游戏对象在ECS被抽象为Entity和一些Component,然而Entity仅仅是一个整型id,Component的添加和删除也仅仅是数组的操作,我们对Component数组中Component的顺序并不敏感,利用SwapAndRemove的方式能给组件的添加和
移除带来o(1)级别的效率,创建和销毁并不需要执行额外的逻辑,这一点非常好,我们可总结为,ECS适合大量对象频繁创建销毁的使用场景。游戏世界中虽然对象数据可能非常庞大,但是他们有着非常多的共性,比如,物理是玩家、野怪、载具他们
都有移动的功能,战斗中的对象都有伤害机制,这些都可以统一抽象成同一个Component,并使用用一个System来处理,我们知道ECS中遍历同一类型的组件是非常高效的,这一点游戏领域跟ECS是贴合的。很多游戏中都有buff机制,在ECS中
一个特定的buff可以抽象为一个Component,buff的产生和实效可以抽象为ECS中的Component的创建和销毁,非常方便。ECS提倡“组合优于继承”的设计理念,“组合”这个概念在游戏世界中也非常常见,比如,移动组件、伤害组件、敌人标签组件就
可以组合成一个简单的敌对小兵,稍作改变,我们把敌人标签换成队友标签,那么我们可以得到一个为你战斗的队友,ECS中Component的删除和新增是一个基础特性,所以你可以很轻易的完成这种基于组合的创造,而不用书写多余的逻辑。此外,
游戏世界中也是具有明显的隔离特性。至此我们可以总结一下游戏世界与ECS的显著契合点, 当然列举的可能并不全面:1、大量对象 2、频繁创建销毁 3、共性行为,比如移动、伤害、buff机制 4、组合在游戏中的抽象优势 5、明确的数据隔离特性
### 基本架构
![img.png](res/img4.png)
### 基本概念
ECS的整体方向是明确的,但是根据不同的ECS实现,会有一些细节上的差异,比如,命名上的差异,Component存储结构上的差异,甚至可能会根据实际的需要或者开发语言的原因妥协一些特性或者添加一下新的特性,下面会介绍一下,我们当前框架下
的一些基本概念。
#### World
World是ECS的核心,用来存储所有的Entity和Component,并管理所有的System, 它类似于通用开发中的管理器。框架中World分为SyncWorld和AsyncWorld,根据不同的使用场景,尤其是Go语言非常轻松的写出多线程逻辑的时候,我们
在调用ECS接口时,可能是同一线程,也可能是不同的线程。如果开发的入口逻辑可以保证是同一线程,那么可以选择使用SyncWorld,如果不能,那么选用AsyncWorld,框架会自动帮你处理好ECS与外围系统的交互安全问题。举一个很常见的例子,比如
使用框架构建一个客户端系统的时候,往往会有一个明确的主线程,此时使用SyncWorld就可以了,但如果是后台服务,我们知道在Go语言的实践中,往往不同的网络消息会交由不同的线程(goroutine,轻量线程,跟协程还是有所区别,我统一叫线程)来处理,
ECS内部能够很好的处理多线程,但是ECS外部的调用,需要由开发者去控制,此时选择AsyncWorld就可以了,AsyncWorld与SyncWorld的区别在于,AsyncWorld是线程安全的。
#### Entity
Entity是ECS中的基本单元,它是一个64位的整数ID,用来标识一个对象。Entity的ID由World来分配,在任意时刻,Entity的ID都是唯一的,但是不严格保证全局唯一,所以应当注意使用途中不应该用Entity代替UID使用,原因是ID低32位被设计成可复用
,ECS系统单次运行时所申请的ID是唯一的,但重复运行后会重复。有些ECS框架会使用GUID或者使用雪花算法等来生成全局唯一的ID,但我们没有这样做,原因后续会讲到。
#### Component
Component是ECS中的数据单元,它是一个结构体,用来存储数据,每个Component都有一个唯一的类型ID,用来标识这个Component的类型,Component的类型ID由World来分配,同样的,Component的类型ID也是唯一的,但不严格保证全局唯一。
#### System
System是ECS中的逻辑单元,在其他的ECS框架实现中,可能System会简单到就是一个函数,实际上这足够满足ECS对系统的描述,但在我们的框架中,System会相对复杂一些,但是他的本质还是处理Component的逻辑,后面会介绍具体实现。
#### Utility
Utility是ECS中的工具单元,但与其他的ECS实现的不同,我们的Utility不再是公共处理函数的集合,他是System的扩展,每一个Utility都与一个特定的System绑定,我们的原则中,仅有System可以直接操作Component,Component只能被System读写,
且System总是独立的,但也总是会有例外,Utility就是这个沟通的桥梁,比如我们明确SystemA对Component1感兴趣,那么SystemA是可以光明正大获取到Component1,但其他地方无法获取到Component1,此时我们需要使用绑定在SystemA上的Utility来借助
SystemA获取Component1,并做一定的操作。Utility的作用非常简单,就是一个桥梁,系统与外界沟通的桥梁,同时也让开发者清楚的感受到各个逻辑直接的界限,突出System独立的这个概念,可以有效的让某一特定的功能内聚到同一System内,降低耦合,
这是强制的限制,但是开发者可能通过语言层面的技巧突破这个限制,但我们希望开发者能够遵守这个原则,这样可以让代码更加清晰,更加易于维护。
#### Shape
Shape是ECS中的辅助单元,用来描述一组同属同一Entity的Component,也有ECS系统称之为Archetype或Sibling, 但是在我们这里不够准确,他们描述的是同一Entity所有Component的组合,而Shape描述的是一个子集,目的是用于同时获取一组Component,
当某System的某个操作,会固定操作同一Entity的一组组件时,建议使用Shape单元, 通过Shape对象可以获取到Component组迭代器,实现了类似Filter的功能,但我们不准备提供完整的Filter体系,因为我们尽可能不做过于复杂的筛选,API一旦提供
就避免不了滥用,当确有需要的时候,通过遍历实现,也非常简单。我们的筛选机制是通过系统Requirement的组合和Shape完成。
#### FixedString
FixedString是一个固定长度的字符串,适用于Component中的字符串,语言内置string是引用类型,如果内存的方式转移组件,那么内置string会带来一些问题。
#### “下一帧生效"
这是一个非常重要的概念,我们的ECS框架中,对Entity的Component创建、删除操作都会在下一帧生效。
### 设计目标
* 组件使用连续内存结构,减少cpu cache-miss
* 快速索引,无遍历获取兄弟组件
* 无锁化开发流程,开发者无需考虑并发安全问题
* 并行化System
* 阶段化执行流程,ecs全执行周期包含多个阶段,如start、update、destroy等
* ecs有主线程概念
* 通过语法限制用户行为,而不是靠用户“自觉“
* 整个世界可序列化,能够根据序列化数据恢复世界
* 充分利用Tick空档期

## 使用教程
### 创建一个World
#### SyncWorld
```go
world := ecs.NewSyncWorld()
```
#### AsyncWorld
```go
world := ecs.NewAsyncWorld()
```
### 创建一个Entity
```go
// NewEntity() 创建的是一个Entity
entity := world.NewEntity()
```
### 创建一个Component
```go
// 创建Component时需要嵌套Component[T], T为Component的实际类型,如下面的TestComponent
type TestComponent struct {
    ecs.Component[TestComponent]
    Field1 int
    Field2 int
}
```
### 创建一个System
```go
type TestSystem struct {
	ecs.System[TestSystem]
}

// 所有系统事件都是可选的,如果不需要某个事件,可以不实现

// 系统Init事件
func (w *TestSystem) Init(sic SystemInitConstraint) error {}

// 系统Start事件
func (w *TestSystem) Start(event Event) {}

// 系统PreUpdate事件
func (w *TestSystem) PreUpdate(event Event) {}

// 系统Start事件
func (w *TestSystem) Update(event Event) {}

// 系统SyncUpdate事件
func (w *TestSystem) SyncUpdate(event Event) {}

// 系统PostUpdate事件
func (w *TestSystem) PostUpdate(event Event) {}

```
我们的ECS是阶段化的执行流程,提供了丰富的阶段性事件,当然最常用的是Update事件,当需要额外控制System先后顺序的时候,可以选择合适的事件混合使用。  
#### 阶段事件
* Init
* SyncBeforeStart
* Start
* SyncAfterStart
* SyncBeforePreUpdate
* PreUpdate
* SyncAfterPreUpdate
* SyncBeforeUpdate
* Update
* SyncAfterUpdate
* SyncBeforePostUpdate
* PostUpdate
* SyncAfterPostUpdate
* SyncBeforeDestroy
* Destroy
* SyncAfterDestroy
#### 阶段特征
| 阶段事件 | 执行次数 | 执行时机              | 同步 | 异步 |
| --- |------|-------------------| --- |---|
| Init | 1    | World Init阶段      | √      | × |
| SyncBeforeStart | 1    | World Update阶段    |  √         | × |
| Start | 1    | World Update阶段          | ×          | √ |
| SyncAfterStart | N    | World Update阶段          | √          | × |
| SyncBeforePreUpdate | N    | World Update阶段  | √          | × |
| PreUpdate | N    | World Update阶段  | ×          | √ |
| SyncAfterPreUpdate | N    | World Update阶段  | √          | × |
| SyncBeforeUpdate | N    | World Update阶段     | √          | × |
| Update | N    | World Update阶段     | ×          | √ |
| SyncAfterUpdate | N    | World Update阶段     | √          | × |
| SyncBeforePostUpdate | N    | World Update阶段 | √          | × |
| PostUpdate | N    | World Update阶段 | ×          | √ |
| SyncAfterPostUpdate | N    | World Update阶段 | √          | × |
| SyncBeforeDestroy | 1    | World Destroy阶段    | √          | × |
| Destroy | 1    | World Destroy阶段    | ×          | √ |
| SyncAfterDestroy | 1    | World Destroy阶段    | √          | × |

### 使用Utility与System交互
“Component中只有数据,System只有逻辑,只有System可以操作Component”这是我们ECS设计的指导思路,当所有的输入都来源于ECS的内部,所有系统之间
都不需要通过Component以外的方式进行交互,那么一切都将会是很美好的。当通常情况下,我们的系统会越来越复杂,不可避免会打破这个规则,但是我们并不希望
破坏我们系统的独立性,逻辑的内聚性,所以我们提供了Utility来解决这个问题。Utility与System一一对应,他可以作为单例的身份存在,同时也是与System
沟通的桥梁。在[ECS与外围系统交互](#ECS与外围系统交互)中,我们列举一个常见的例子的使用场景,在开发后台系统时,网络事件收到数据包后,需要交给我们
的ECS处理,那么就需要一次沟通,将数据包安全的交给ECS,这个时候就需要Utility来配合完成这个工作。我们先看如何创建一个Utility,并与对应的系统关联。
```go
type TestUtility struct {
    Utility[TestUtility]
}

func (u *TestUtility) ChangeName(entity Entity, name string) {
	sys := u.GetSystem()
	c := ecs.GetComponent[TestComponent](sys, entity)
	if c == nil {
		return
	}
	c.Name = name
	Log.Infof("Name changed, new:%s", name)
}

func main() {
        world := ......
		
        // 获取并使用Utility
        utility, _ :=ecs.GetUtility[TestUtility](world)
        utility.ChangeName(entity, "test")
		
        ......
}
```
需要注意的是,Utility与System是一一对应的, 需要再TestSystem的Init事件中绑定Utility,当然在不需要时可省略。
```go
func (s *TestSystem) Init(si SystemInitializer) {
    // 绑定Utility
    ecs.BindUtility[TestUtility](si)
}
```
### ECS与外围系统交互
World是ECS外部系统唯一能获取到跟对象,他是外部与ECS交互的入口,World分为SyncWorld和AsyncWorld,
SyncWorld适用于同步环境,AsyncWorld适用于异步环境。通常情况下,外部系统与ECS的交互流程如下:  
&emsp;&emsp;外部-->SyncWorld/AsyncWorld-->Utility-->Component  
直接看下面的例子,可能会觉得不太好理解,根据ECS的设计,只有System能修改Component,但是我们并不能直接获取到System实例,这是有意为之,因为System处于并行状态,不能随便的被调用,
但是ECS为此设计了Utility,Utility作为System的补充和桥梁,也可以修改Component,示例中```u.ChangeName(entity, name)```正是Utility对Component的
操作,我们先忽略其内部实现。
#### SyncWorld
```go
type TestUtility struct {
	Utility[TestUtility]
}

func (u *TestUtility) ChangeName(entity Entity, name string) {
	sys := u.GetSystem()
	c := GetComponent[TestComponent1](sys, entity)
	if c == nil {
		return
	}
	old := c.Name.String()
	c.Name.Set(name)
	Log.Infof("Name changed, old: %s, new:%s", old, name)
}

func main() {
    // 获取配置并创建世界
    config := NewDefaultWorldConfig()
    world := NewSyncWorld(config)

    // 注册系统
    RegisterSystem[TestSystem1](world)

    // 启动世界
    world.Startup()

    // 添加一下测试用的实体
    entities := make([]Entity, 100)
    for i := 0; i < 100; i++ {
        e1 := world.NewEntity()
        world.Add(e1, &TestComponent1{}, &TestComponent2{}, &TestComponent3{})
        entities[i] = e1
    }

    // 尝试更新世界,使实体与他们的组件生效
    world.Update()

    // 获取Utility,并调用Utility的ChangeName方法,修改实体的Name字段
    getter := world.GetUtilityGetter()
    u, ok := GetUtility[TestUtility](getter)
	if ok {
        u.ChangeName(entities[0], "name0")
    }

    // 持续更新你的世界
    for {
        world.Update()
        time.Sleep(time.Second)
    }
}
```
#### AsyncWorld
AsyncWorld与SyncWorld不同,AsyncWorld被应用于多线程环境中,具备串行化的能力。
相较于SyncWorld,AsyncWorld多了一个Sync方法,用于将异步的操作同步化。
同时AsyncWorld不在需要我们手动调用Update方法,他会自动更新,在使用SyncWorld时我们明确的清楚每次调用Update时都处于同一线程,
但是在AsyncWorld中,Update方法可能会被多个线程调用,会导致我们的ECS混乱,这也是设计AsyncWorld的主要原因。
```go
type TestUtility struct {
    Utility[TestUtility]
}

func (u *TestUtility) ChangeName(entity Entity, name string) {
    sys := u.GetSystem()
    c := GetComponent[TestComponent1](sys, entity)
    if c == nil {
        return
    }
    old := c.Name.String()
    c.Name.Set(name)
    Log.Infof("Name changed, old: %s, new:%s", old, name)
}

func main() {
    // 获取配置并创建世界
    config := NewDefaultWorldConfig()
    world := NewSyncWorld(config)

    // 注册系统
    RegisterSystem[TestSystem1](world)

    // 启动世界
    world.Startup()

    // 添加一下测试用的实体
    entities := make([]Entity, __worldTest_Entity_Count)
    world.Sync(func(gaw SyncWrapper) {
        for i := 0; i < __worldTest_Entity_Count; i++ {
            e1 := gaw.NewEntity()
            gaw.Add(e1, &__world_Test_C_1{}, &__world_Test_C_2{}, &__world_Test_C_3{})
            entities[i] = e1
        }
    })
	
    // 获取Utility,并调用Utility的ChangeName方法,修改实体的Name字段
    time.Sleep(time.Second * 2)
    world.Sync(func(gaw SyncWrapper) {
        u, ok := GetUtility[__world_Test_U_Input](gaw)
        if !ok {
            return
        }
        u.ChangeName(entities[0], "name2")
    })
	
    for {
        time.Sleep(time.Second)
    }
}

```
### 如何处理System的执行顺序
我们的ECS中有两种方式可以控制逻辑的顺序:
* 选择合适的阶段
* 指定系统的执行顺序  

&emsp;&emsp;文档的前方我们已经提到过,ECS中的设计有16个阶段,非常丰富,足以满足绝大部分的开发需求,其中包含Sync开头的同步点阶段,非Sync开头的异步并行阶段,
详细请看[支持的阶段事件](#支持的阶段事件)。值得注意的是,当业务逻辑需要某个阶段时可以实现对应阶段的事件,不需要时无需实现。
```go
type TestSystem1 struct {
    ecs.System[TestSystem1]
}

func (s *TestSystem1) Init(si SystemInitConstraint) error {
    Log.Info("TestSystem1 Init")
}

func (s *TestSystem1) Start(event Event) {
    Log.Info("TestSystem1 Start")
}

func (s *TestSystem1) Update(event Event) {
    Log.Info("TestSystem1 Update")
}

// 前缀为Sync的阶段为同步阶段,同步阶段中ECS系统单线程执行,所以所有的操作都是安全的。
func (s *TestSystem1) SyncAfterUpdate(event Event) {
    Log.Info("TestSystem1 SyncAfterUpdate")
}
```

&emsp;&emsp;当丰富的阶段任然无法满足你的需求,比如你需要在同一阶段中指定系统的执行顺序,
这时你可以在系统的注册阶段指定其顺序。先后循序为指定Order数值由小到大执行,如下面代码所以,
系统的执行顺序为:TestSystem2 -> TestSystem3 -> TestSystem1。
```go
world := NewSyncWorld(config)

// 内置顺序选项, 默认为Append
// OrderFront   Order = 所有系统的前面
// OrderAppend  Order = 所有系统的后面

// 你可以使用的顺序是: 0-99999999
RegisterSystem[TestSystem1](world, Order(150))
RegisterSystem[TestSystem2](world, Order(5))
RegisterSystem[TestSystem3](world, Order(100))
```

### 如何选择不同类型的Component
#### 常规组件
正常情况下,我们的数据都属于正常组件这一类,比如玩家的位置、等级、血量等,这些数据会伴随实体的全生命周期, 知道实体被销毁。
类于Buff这样的组件,不会伴随实体全生命周期,但会持续一段时间,也可以视作常规组件。常规组件的定义方式:
```go
type Position struct {
    ecs.Component[Position]
    X float32
    Y float32
    Z float32
}

type HP struct {
    ecs.Component[HP]
    Value int32
}
```
#### Free组件
Free组件是特殊的组件之一,它不会被添加到实体中,它游离态的组件,通常可作为一些临时数据的载体与多个系统共享。典型的用法是将Free组件
作为系统间共享数据的“队列”,比如,客户端通过网络上行的移动指令,在实际的开发中往往需要将这些指令缓存起来,多个指令同时处理。
```go
type MoveCommand struct {
    ecs.FreeComponent[MoveCommand]
    ...
}
```
#### Disposable组件
Disposable组件特殊的组件之一,它是一次性组件,会在每帧结束后清理。假设我们需要获取新增的Entity,可以在创建Entity时,同时添加Disposable组件,
在Update中获取该Disposable组件集合,从而获取新增的Entity。Disposable组件
会在每一帧被清空,可以活的当做一次性tag使用。
```go
type TagNew struct {
    ecs.DisposableComponent[TagNew]
    ...
}
```
#### FreeDisposable组件
FreeDisposable组件是Free组件与Disposable组件的结合体,它是一次性的游离态组件,会在每帧结束后清理。
```go
type FreeDisposable struct {
    ecs.FreeDisposableComponent[FreeDisposable]
    ...
}
```
### 系统中获取组件的方式
获取Component是System中最常用的操作, 获取和遍历Component的效率是评价ECS框架优良最重要的指标之一,是ECS框架设计的核心和重心。
我们的ECS框架提供了非常高效的获取和遍历Component的操作,做到了寻址级别的查询效率,同时连续内存的存储方式,使得遍历Component的性能也非常优秀。
我们提供了两种方式获取和筛选Component。
* 通过 GetComponent 获取指定Entity的指定Component
```go
c := ecs.GetComponent[TestComponent1](entity)
```
* 通过 GetComponentAll和GetRelated获取主要组件和其关联组件。
```go
type TestSystem1 struct {
    ecs.System[TestSystem1]
}

func (s *TestSystem1) Init(si SystemInitConstraint) error {
    // 设置系统感兴趣的组件
    s.SetRequirements(si, &TestComponent1{}, &TestComponent1{}, &TestComponent1{})
}

func (s *TestSystem1) Update(event Event) {
    Log.Info("TestSystem1 Update")

    iter := ecs.GetComponentAll[TestComponent1](w)
    for c := iter.Begin(); !iter.End(); c = iter.Next() {
        c2 := ecs.GetRelated[TestComponent2](w, c.owner)
        if c2 == nil {
            continue
        }
		
        c.Field1 += 1
        c.Field2 += 1
    }
}
```
* 通过 Shape同时获取兄弟组件, 这种方式非常方便的获取到同一Entity的多个Component,但不如第一种方式灵活。
```go
type Shape1 struct {
    TestComponent1 *TestComponent1
    TestComponent2 *TestComponent2
    TestComponent3 *TestComponent3
}

type TestSystem1 struct {
    ecs.System[TestSystem1]
	shp *ecs.Shape[Shape1]
}

func (s *TestSystem1) Init(si SystemInitConstraint) {
    // 设置系统感兴趣的组件
    s.SetRequirements(si, &TestComponent1{}, &TestComponent1{}, &TestComponent1{})
    // 初始化ShapeGetter
    s.shp = NewShape[Shape1](si)
}

func (s *TestSystem1) Update(event Event) {
    Log.Info("TestSystem1 Update")

    iter := s.shp.Get()
    for shp := iter.Begin(); !iter.End(); shp = iter.Next() {
        c.TestComponent1.Field1 += 1
        c.TestComponent2.Field1 += 1
        c.TestComponent3.Field1 += 1
    }
}
```
### 系统间的数据流动
(努力完善中)
### 一个完整的例子
(努力完善中)
## Benchmark

(努力完善中)
## API文档
(努力完善中)
## 设计细节
(努力完善中)
### 架构
(努力完善中)
### 日志
(努力完善中)
### World
(努力完善中)
### Entity
* Entity复用
* 不保证全局唯一
* Entity的ID是一个64位的整数,高32位是世界ID,低32位是EntityID,这样设计的目的是为了支持多个世界的存在,比如,我们可以在一个世界中创建一个Entity,然后把这个Entity
### Component
(努力完善中)
### System
(努力完善中)
### Utility
(努力完善中)
### Shape
(努力完善中)
### Compound
(努力完善中)
### 一次Update的执行流程
(努力完善中)
### 从添加Component到生效
(努力完善中)
### 容器
* unordered_set
* sparse_array
* ordered_int_set
### 迭代器
(努力完善中)
### 协程池
(努力完善中)
### ECS序列化和反序列化
(努力完善中)
### ECS中的并行
(努力完善中)
### EntityID的管理
(努力完善中)
### 如何行为限制
* 通过状态量,限制某些API的访问时期,比如某些API只能在初始化阶段执行,否则将收到错误警告
* 通过guard参数,某些API的调用需要接受一个guard参数,guard参数的作用域受到框架的限制,比如在System的Init事件中
, guard生命周期为Init事件的执行周期,Init执行结束,guard参数失效,传递、转存无法维持guard参数的有效性,因此限制
了某些API仅能在特定的事件中被调用。(需要补充实现细节代码介绍)
### 优化器
* 优化器的作用,尽可能使得ecs框架内部的数据结构尽可能连续,减少cpu cache-miss
* 优化器工作时间点,tick驱动时,利用每次update的富裕时间进行优化工作
* 优化器工作内容,对于每个Component的数据结构,进行连续化优化
### 统计器
* 统计器的内容,统计ecs框架内部的运行状态,比如每个System的执行时间,每个Component的内存占用等
* 作用,帮助开发期调试、性能优化,配合优化器完整优化工作
## 特别注意
* 重复添加Component,会失败
* 同一帧内,多次移除、添加、移除...操作只会保留最终结果,因为“下一帧生效”会丢失中间过程,即使不会丢失,也没有实际的意义,建议避免这样的操作。
* Component所有成员变量都应该是值类型,string是引用类型,需要字符串类型时请使用 框架内的FixedString类型。
## 存在的一些问题
* 稀疏数组的内存占用问题
* EntityInfo的修改需要再同步点进行
* 不支持不对等tick,不存在多层次tick,比如A系统tick间隔50ms,B系统tick间隔30ms
* 并行时task的拆分粒度固定,不支持动态调整,优化器实现后,可以根据优化器的结果,动态调整task的拆分粒度
* 并行退化,当开发者的系统依赖混乱,会导致系统关联度过高,框架调度时,会将有数据竞争的系统放到同一个线程中执行,从而导致并行退化,
最糟糕的情况是,退化为单线程系统
* 行为限制的缺失,由于golang语言的特性,无法严格得按照ECS的设计思路限制开发者的行为,开发者必须对数据驱动有一定的了解,无法严格
的使用语法规避潜在的危险行为
## 写在最后
欢迎大家提出宝贵意见,帮助完善ecs框架,这是一个长期和持续的过程,有不足或错误的地方,欢迎指正,欢迎PR。

## TODO
* [ ] 优化器实现
* [ ] 统计器完善
* [ ] 代码覆盖率
* [ ] world序列化
* [ ] 更新Atomic
* [ ] 测试用例混乱
* [ ] Example FakeGame 暂不可用


================================================
FILE: chunk.go
================================================
package ecs

const (
	ChunkSize uintptr = 1024 * 16
	//ChunkSize  int64   = 512
	EntitySize uintptr = 8
)

const (
	ChunkAddCodeSuccess int = iota
	ChunkAddCodeFull
	ChunkAddCodeInvalidElement
)

type Chunk[T ComponentObject] struct {
	data    []T
	ids     map[int64]int64
	idx2id  map[int64]int64
	len     int64
	max     int64
	eleSize uintptr
	pend    uintptr
	pre     *Chunk[T]
	next    *Chunk[T]
}

func NewChunk[T ComponentObject]() *Chunk[T] {
	size := TypeOf[T]().Size()
	max := ChunkSize / size
	c := &Chunk[T]{
		data:    make([]T, max, max),
		eleSize: size,
		max:     int64(max),
		ids:     make(map[int64]int64),
		idx2id:  make(map[int64]int64),
	}
	return c
}

func (c *Chunk[T]) Add(element *T, id int64) (*T, int) {
	if uintptr(len(c.data)) >= c.pend+c.eleSize {
		c.data[c.pend+1] = *element
	} else {
		return nil, ChunkAddCodeFull
	}
	idx := c.len
	c.ids[id] = idx
	c.idx2id[idx] = id
	real := &(c.data[c.pend])
	c.len++
	c.pend += c.eleSize
	return real, ChunkAddCodeSuccess
}

func (c *Chunk[T]) Remove(id int64) {
	if id < 0 {
		return
	}
	idx, ok := c.ids[id]
	if !ok {
		return
	}
	lastIdx := c.len - 1
	lastId := c.idx2id[lastIdx]

	c.ids[lastId] = idx
	c.idx2id[idx] = lastId
	delete(c.idx2id, lastIdx)
	delete(c.ids, id)

	c.data[idx], c.data[lastIdx] = c.data[lastIdx], c.data[idx]
	c.len--
}

func (c *Chunk[T]) RemoveAndReturn(id int64) *T {
	if id < 0 {
		return nil
	}
	idx, ok := c.ids[id]
	if !ok {
		return nil
	}
	lastIdx := c.len - 1
	lastId := c.idx2id[lastIdx]
	c.ids[lastId] = idx
	c.idx2id[idx] = lastId
	delete(c.idx2id, lastIdx)
	delete(c.ids, id)

	c.data[idx], c.data[lastIdx] = c.data[lastIdx], c.data[idx]
	r := &(c.data[lastIdx])
	c.len--
	c.pend -= c.eleSize
	return r
}

func (c *Chunk[T]) MoveTo(target *Chunk[T]) []int64 {
	moveSize := uintptr(0)
	if c.len < c.max {
		moveSize = uintptr(c.len)
	} else {
		moveSize = uintptr(c.max)
	}
	copy(target.data[target.pend:target.pend+moveSize], c.data[c.pend-moveSize:c.pend])

	var moved []int64
	for i := int64(0); i < int64(moveSize); i++ {
		idx := c.len - int64(moveSize) + i
		id := c.idx2id[idx]
		moved = append(moved, id)
	}

	target.pend += moveSize * c.eleSize
	c.pend -= moveSize * c.eleSize
	target.len += int64(moveSize)
	c.len -= int64(moveSize)

	return moved
}

func (c *Chunk[T]) Get(id int64) *T {
	idx, ok := c.ids[id]
	if !ok {
		return nil
	}
	return &(c.data[idx])
}

func (c *Chunk[T]) GetByIndex(idx int64) *T {
	if idx < 0 || idx >= c.len {
		return nil
	}
	return &(c.data[idx])
}

func (c *Chunk[T]) Len() int64 {
	return c.len
}


================================================
FILE: collection_iter.go
================================================
package ecs

import "unsafe"

type Iterator[T any] interface {
	Begin() *T
	Val() *T
	Next() *T
	End() bool
}

type Iter[T any] struct {
	head     unsafe.Pointer
	data     []T
	len      int
	offset   int
	pend     uintptr
	cur      *T
	curTemp  T
	eleSize  uintptr
	readOnly bool
}

func EmptyIter[T any]() Iterator[T] {
	return &Iter[T]{}
}

func (i *Iter[T]) End() bool {
	if i.offset >= i.len || i.len == 0 {
		return true
	}
	return false
}

func (i *Iter[T]) Begin() *T {
	if i.len != 0 {
		i.offset = 0
		if i.readOnly {
			i.curTemp = i.data[0]
			i.cur = &i.curTemp
		} else {
			i.cur = &(i.data[0])
		}
	}
	return i.cur
}

func (i *Iter[T]) Val() *T {
	return i.cur
}

func (i *Iter[T]) Next() *T {
	i.offset++
	i.pend += i.eleSize
	if !i.End() {
		if i.readOnly {
			//i.curTemp = i.data[i.offset]
			i.curTemp = *(*T)(unsafe.Add(i.head, i.pend))
			i.cur = &i.curTemp
		} else {
			//i.cur = &(i.data[i.offset])
			i.cur = (*T)(unsafe.Add(i.head, i.pend))
		}
	} else {
		i.cur = nil
	}
	return i.cur
}


================================================
FILE: component.go
================================================
package ecs

import (
	"fmt"
	"reflect"
	"unsafe"
)

const (
	h4   = uint8(240)
	l4   = uint8(15)
	zero = uint8(0)
)

type ComponentState uint8

const (
	ComponentStateInvalid ComponentState = iota
	ComponentStateActive
	ComponentStateDisable
)

type ComponentType uint8

const (
	ComponentTypeFreeMask       ComponentType = 1 << 7
	ComponentTypeDisposableMask ComponentType = 1 << 6
)

const (
	ComponentTypeNormal         ComponentType = 0
	ComponentTypeDisposable                   = 1 | ComponentTypeDisposableMask
	ComponentTypeFree                         = 2 | ComponentTypeFreeMask
	ComponentTypeFreeDisposable               = 3 | ComponentTypeFreeMask | ComponentTypeDisposableMask
)

type EmptyComponent struct {
	Component[EmptyComponent]
}

type IComponent interface {
	Owner() Entity
	Type() reflect.Type

	setOwner(owner Entity)
	setState(state ComponentState)
	setIntType(typ uint16)
	setSeq(seq uint32)
	getState() ComponentState
	getIntType() uint16
	getComponentType() ComponentType
	getPermission() ComponentPermission
	check(initializer SystemInitConstraint)
	getSeq() uint32
	newCollection(meta *ComponentMetaInfo) IComponentSet
	addToCollection(ct ComponentType, p unsafe.Pointer)
	deleteFromCollection(collection interface{})
	isValidComponentType() bool

	debugAddress() unsafe.Pointer
}

type ComponentObject interface {
	__ComponentIdentification()
}

type FreeComponentObject interface {
	__ComponentIdentification()
}

type DisposableComponentObject interface {
	__ComponentIdentification()
}

type FreeDisposableComponentObject interface {
	__ComponentIdentification()
}

type ComponentPointer[T ComponentObject] interface {
	IComponent
	*T
}

type FreeComponentPointer[T FreeComponentObject] interface {
	ComponentPointer[T]
	*T
}

type DisposableComponentPointer[T FreeComponentObject] interface {
	ComponentPointer[T]
	*T
}

type FreeDisposableComponentPointer[T FreeComponentObject] interface {
	ComponentPointer[T]
	*T
}

type FreeComponent[T ComponentObject] struct {
	Component[T]
}

func (f *FreeComponent[T]) getComponentType() ComponentType {
	return ComponentTypeFree
}

type DisposableComponent[T ComponentObject] struct {
	Component[T]
}

func (f *DisposableComponent[T]) getComponentType() ComponentType {
	return ComponentTypeDisposable
}

type FreeDisposableComponent[T ComponentObject] struct {
	Component[T]
}

func (f *FreeDisposableComponent[T]) getComponentType() ComponentType {
	return ComponentTypeFreeDisposable
}

type componentIdentification struct{}

func (c componentIdentification) __ComponentIdentification() {}

type Component[T ComponentObject] struct {
	componentIdentification
	st    uint8
	o1    uint8
	it    uint16
	seq   uint32
	owner Entity
}

func (c *Component[T]) getComponentType() ComponentType {
	return ComponentTypeNormal
}

func (c *Component[T]) addToCollection(ct ComponentType, p unsafe.Pointer) {
	cc := (*ComponentSet[T])(p)
	var ins *T
	if ct&ComponentTypeFreeMask > 0 {
		ins, _ = cc.UnorderedCollection.Add(c.rawInstance())
	} else {
		ins = cc.Add(c.rawInstance(), c.owner)
	}

	if ins != nil {
		(*Component[T])(unsafe.Pointer(ins)).setState(ComponentStateActive)
	}
}

func (c *Component[T]) deleteFromCollection(collection interface{}) {
	cc, ok := collection.(*ComponentSet[T])
	if !ok {
		Log.Info("add to collection, collecion is nil")
		return
	}
	c.setState(ComponentStateDisable)
	cc.Remove(c.owner)
	return
}

func (c *Component[T]) newCollection(meta *ComponentMetaInfo) IComponentSet {
	return NewComponentSet[T](meta)
}

func (c *Component[T]) setOwner(entity Entity) {
	c.owner = entity
}

func (c *Component[T]) rawInstance() *T {
	return (*T)(unsafe.Pointer(c))
}

func (c *Component[T]) instance() IComponent {
	return any((*T)(unsafe.Pointer(c))).(IComponent)
}

func (c *Component[T]) setState(state ComponentState) {
	c.st = (c.st & l4) | (uint8(state) << 4)
}

func (c *Component[T]) getState() ComponentState {
	return ComponentState(c.st & h4 >> 4)
}

func (c *Component[T]) setType(typ ComponentType) {
	c.st = (c.st & h4) | uint8(typ)
}

func (c *Component[T]) getType() ComponentType {
	return ComponentType(c.st & l4)
}

func (c *Component[T]) setIntType(typ uint16) {
	c.it = typ
}

func (c *Component[T]) getIntType() uint16 {
	return c.it
}

func (c *Component[T]) setSeq(seq uint32) {
	c.seq = seq
}

func (c *Component[T]) getSeq() uint32 {
	return c.seq
}

func (c *Component[T]) invalidate() {
	c.setState(ComponentStateInvalid)
}

func (c *Component[T]) active() {
	c.setState(ComponentStateActive)
}

func (c *Component[T]) Owner() Entity {
	return c.owner
}

func (c *Component[T]) Type() reflect.Type {
	return TypeOf[T]()
}

func (c *Component[T]) getPermission() ComponentPermission {
	return ComponentReadWrite
}

func (c *Component[T]) check(initializer SystemInitConstraint) {
	if initializer.isValid() {
		panic("out of initialization stage")
	}
	ins := c.instance()
	if !ins.isValidComponentType() {
		panic("invalid component type")
	}
	sys := initializer.getSystem()
	sys.World().getOrCreateComponentMetaInfo(ins)
	sys.World().getComponentCollection().checkSet(ins)
}

func (c *Component[T]) isValidComponentType() bool {
	typ := c.Type()
	fieldNum := typ.NumField()
	if fieldNum < 1 {
		return false
	}
	for i := 1; i < fieldNum; i++ {
		subType := typ.Field(i).Type
		if !IsPureValueType(subType) {
			return false
		}
	}
	return true
}

func (c *Component[T]) debugAddress() unsafe.Pointer {
	return unsafe.Pointer(c)
}

func (c *Component[T]) ToString() string {
	return fmt.Sprintf("%+v", c.rawInstance())
}


================================================
FILE: component_collection.go
================================================
package ecs

import (
	"reflect"
	"sync"
	"unsafe"
)

type CollectionOperate uint8

const (
	CollectionOperateNone      CollectionOperate = iota
	CollectionOperateAdd                         //add component operation
	CollectionOperateDelete                      //delete component operation
	CollectionOperateDeleteAll                   //delete component by type operation
)

type IComponentCollection interface {
	operate(op CollectionOperate, entity Entity, component IComponent)
	deleteOperate(op CollectionOperate, entity Entity, it uint16)
	getTempTasks() []func()
	clearDisposable()
	getComponentSet(typ reflect.Type) IComponentSet
	getComponentSetByIntType(typ uint16) IComponentSet
	getCollections() *SparseArray[uint16, IComponentSet]
	checkSet(com IComponent)
}

type ComponentCollection struct {
	collections *SparseArray[uint16, IComponentSet]
	world       *ecsWorld
	bucket      int64
	locks       []sync.RWMutex
	opLog       []map[reflect.Type]*opTaskList
}

func NewComponentCollection(world *ecsWorld, k int) *ComponentCollection {
	cc := &ComponentCollection{
		world:       world,
		collections: NewSparseArray[uint16, IComponentSet](),
	}

	for i := 1; ; i++ {
		if c := int64(1 << i); int64(k) < c {
			cc.bucket = c - 1
			break
		}
	}

	cc.bucket = 0

	cc.locks = make([]sync.RWMutex, cc.bucket+1)
	for i := int64(0); i < cc.bucket+1; i++ {
		cc.locks[i] = sync.RWMutex{}
	}
	cc.opLog = make([]map[reflect.Type]*opTaskList, cc.bucket+1)
	cc.initOptTemp()

	return cc
}

func (c *ComponentCollection) initOptTemp() {
	for index := range c.opLog {
		c.locks[index].Lock()
		c.opLog[index] = make(map[reflect.Type]*opTaskList)
		c.locks[index].Unlock()
	}
}

func (c *ComponentCollection) operate(op CollectionOperate, entity Entity, component IComponent) {
	var hash int64
	switch component.getComponentType() {
	case ComponentTypeFree, ComponentTypeFreeDisposable:
		hash = int64((uintptr)(unsafe.Pointer(&hash))) & c.bucket
	case ComponentTypeNormal, ComponentTypeDisposable:
		hash = int64(entity) & c.bucket
	}

	typ := component.Type()
	newOpt := opTaskPool.Get()
	newOpt.target = entity
	newOpt.com = component
	newOpt.op = op

	b := c.opLog[hash]

	c.locks[hash].Lock()
	defer c.locks[hash].Unlock()

	tl, ok := b[typ]
	if !ok {
		tl = &opTaskList{}
		b[typ] = tl
	}

	tl.Append(newOpt)
}

func (c *ComponentCollection) deleteOperate(op CollectionOperate, entity Entity, it uint16) {
	var hash int64
	meta := c.world.componentMeta.GetComponentMetaInfoByIntType(it)
	if meta.componentType&ComponentTypeFreeMask > 0 {
		hash = int64((uintptr)(unsafe.Pointer(&hash))) & c.bucket
	} else {
		hash = int64(entity) & c.bucket
	}

	typ := meta.typ
	newOpt := opTaskPool.Get()
	newOpt.target = entity
	newOpt.com = nil
	newOpt.op = op

	b := c.opLog[hash]

	c.locks[hash].Lock()
	defer c.locks[hash].Unlock()

	tl, ok := b[typ]
	if !ok {
		tl = &opTaskList{}
		b[typ] = tl
	}

	tl.Append(newOpt)
}

func (c *ComponentCollection) clearDisposable() {
	disposable := c.world.componentMeta.GetDisposableTypes()
	for r, _ := range disposable {
		meta := c.world.getComponentMetaInfoByType(r)
		if meta.componentType&ComponentTypeFreeMask > 0 {
			continue
		}
		set := c.collections.Get(meta.it)
		if set == nil {
			return
		}
		(*set).Range(func(com IComponent) bool {
			info, ok := c.world.entities.GetEntityInfo(com.Owner())
			if ok {
				info.removeFromCompound(meta.it)
			}
			return true
		})

		(*set).Clear()
	}
}

func (c *ComponentCollection) clearFree() {
	free := c.world.componentMeta.GetFreeTypes()
	for r, _ := range free {
		meta := c.world.getComponentMetaInfoByType(r)
		set := c.collections.Get(meta.it)
		if set == nil {
			return
		}
		(*set).Clear()
	}
}

func (c *ComponentCollection) getTempTasks() []func() {
	combination := make(map[reflect.Type]*opTaskList)

	for i := 0; i < len(c.opLog); i++ {
		c.locks[i].RLock()
		for typ, list := range c.opLog[i] {
			if list.Len() == 0 {
				continue
			}
			if _, ok := combination[typ]; ok {
				combination[typ].Combine(list)
			} else {
				combination[typ] = list.Clone()
			}
			list.Reset()
		}

		c.locks[i].RUnlock()
	}

	var tasks []func()
	for typ, list := range combination {
		taskList := list
		if taskList.Len() == 0 {
			continue
		}
		meta := c.world.getComponentMetaInfoByType(typ)
		setp := c.collections.Get(meta.it)
		if setp == nil {
			c.checkSet(taskList.head.com)
			setp = c.collections.Get(meta.it)
		}

		fn := func() {
			c.opExecute(taskList, *setp)
		}
		tasks = append(tasks, fn)
	}

	fn := func() {
		for typ, list := range combination {
			meta := c.world.getComponentMetaInfoByType(typ)
			for task := list.head; task != nil; task = task.next {
				if task.op == CollectionOperateDelete {
					continue
				}
				info, ok := c.world.getEntityInfo(task.target)
				if ok {
					switch task.op {
					case CollectionOperateAdd:
						switch task.com.getComponentType() {
						case ComponentTypeNormal, ComponentTypeDisposable:
							info.addToCompound(meta.it)
						}
					case CollectionOperateDelete:
						switch task.com.getComponentType() {
						case ComponentTypeNormal, ComponentTypeDisposable:
							info.removeFromCompound(meta.it)
						}
					}
				}
			}
		}
	}
	tasks = append(tasks, fn)
	return tasks
}

func (c *ComponentCollection) opExecute(taskList *opTaskList, collection IComponentSet) {
	meta := collection.GetElementMeta()
	for task := taskList.head; task != nil; task = task.next {
		switch task.op {
		case CollectionOperateAdd:
			task.com.setIntType(meta.it)
			task.com.setOwner(task.target)
			task.com.addToCollection(task.com.getComponentType(), collection.pointer())
		case CollectionOperateDelete:
			if meta.componentType&ComponentTypeFreeMask == 0 {
				collection.Remove(task.target)
			}
		case CollectionOperateDeleteAll:
			collection.Clear()
		}
	}
	next := taskList.head
	for next != nil {
		task := next
		next = next.next
		opTaskPool.Put(task)
	}
	taskList.Reset()
}

func (c *ComponentCollection) getComponentSet(typ reflect.Type) IComponentSet {
	meta := c.world.getComponentMetaInfoByType(typ)
	return *(c.collections.Get(meta.it))
}

func (c *ComponentCollection) getComponentSetByIntType(it uint16) IComponentSet {
	return *(c.collections.Get(it))
}

func (c *ComponentCollection) getCollections() *SparseArray[uint16, IComponentSet] {
	return c.collections
}

func (c *ComponentCollection) checkSet(com IComponent) {
	typ := com.Type()
	meta := c.world.getComponentMetaInfoByType(typ)
	isExist := c.collections.Exist(meta.it)
	if !isExist {
		set := com.newCollection(meta)
		c.collections.Add(set.GetElementMeta().it, &set)
	}
}


================================================
FILE: component_getter.go
================================================
package ecs

import (
	"reflect"
	"unsafe"
)

type GetterCache struct {
	indices []reflect.Type
	values  []unsafe.Pointer
}

func NewGetterCache(initCap ...int) *GetterCache {
	cap := 0
	if len(initCap) > 0 {
		cap = initCap[0]
	}
	return &GetterCache{
		indices: make([]reflect.Type, 0, cap),
		values:  make([]unsafe.Pointer, 0, cap),
	}
}

func (g *GetterCache) Add(key reflect.Type, value unsafe.Pointer) {
	g.indices = append(g.indices, key)
	g.values = append(g.values, value)
}

func (g *GetterCache) Remove(key reflect.Type) {
	idx := -1
	for i, t := range g.indices {
		if t == key {
			idx = i
			break
		}
	}
	if idx == -1 {
		return
	}
	// remove from indices
	g.indices = append(g.indices[:idx], g.indices[idx+1:]...)
	// remove from values
	g.values = append(g.values[:idx], g.values[idx+1:]...)
}

func (g *GetterCache) Get(key reflect.Type) unsafe.Pointer {
	for i, t := range g.indices {
		if t == key {
			return g.values[i]
		}
	}
	return nil
}

type ComponentGetter[T ComponentObject] struct {
	permission ComponentPermission
	set        *ComponentSet[T]
}

func NewComponentGetter[T ComponentObject](sys ISystem) *ComponentGetter[T] {
	typ := TypeOf[T]()

	r, isRequire := sys.GetRequirements()[typ]
	if !isRequire {
		return nil
	}
	getter := &ComponentGetter[T]{}
	seti := sys.World().getComponentSet(typ)
	if seti == nil {
		return nil
	}
	getter.set = seti.(*ComponentSet[T])
	getter.permission = r.getPermission()
	return getter
}

func (c *ComponentGetter[T]) Get(entity Entity) *T {
	if c.permission == ComponentReadOnly {
		return &(*c.set.getByEntity(entity))
	} else {
		return c.set.getByEntity(entity)
	}
}


================================================
FILE: component_meta.go
================================================
package ecs

import (
	"fmt"
	"reflect"
)

func GetComponentMeta[T ComponentObject](world IWorld) *ComponentMetaInfo {
	return world.getComponentMetaInfoByType(TypeOf[T]())
}

type ComponentMetaInfo struct {
	it            uint16
	componentType ComponentType
	o1            uint8
	typ           reflect.Type
}

type componentMeta struct {
	world      *ecsWorld
	seq        uint16
	types      map[reflect.Type]uint16
	infos      *SparseArray[uint16, ComponentMetaInfo]
	disposable map[reflect.Type]uint16
	free       map[reflect.Type]uint16
}

func NewComponentMeta(world *ecsWorld) *componentMeta {
	return &componentMeta{
		world:      world,
		seq:        0,
		types:      make(map[reflect.Type]uint16),
		infos:      NewSparseArray[uint16, ComponentMetaInfo](),
		disposable: map[reflect.Type]uint16{},
		free:       map[reflect.Type]uint16{},
	}
}

func (c *componentMeta) CreateComponentMetaInfo(typ reflect.Type, ct ComponentType) *ComponentMetaInfo {
	c.world.checkMainThread()
	c.seq++
	info := &ComponentMetaInfo{}
	info.it = c.seq
	info.componentType = ct
	info.typ = typ

	c.types[typ] = info.it
	info = c.infos.Add(info.it, info)

	if ct&ComponentTypeDisposableMask > 0 {
		c.disposable[typ] = info.it
	}
	if ct&ComponentTypeFreeMask > 0 {
		c.free[typ] = info.it
	}

	return info
}

func (c *componentMeta) GetDisposableTypes() map[reflect.Type]uint16 {
	return c.disposable
}

func (c *componentMeta) GetFreeTypes() map[reflect.Type]uint16 {
	return c.free
}

func (c *componentMeta) Exist(typ reflect.Type) bool {
	_, ok := c.types[typ]
	return ok
}

func (c *componentMeta) GetOrCreateComponentMetaInfo(com IComponent) *ComponentMetaInfo {
	it, ok := c.types[com.Type()]
	if !ok {
		it = c.CreateComponentMetaInfo(com.Type(), com.getComponentType()).it
	}
	return c.infos.Get(it)
}

func (c *componentMeta) GetComponentMetaInfoByIntType(it uint16) *ComponentMetaInfo {
	info := c.infos.Get(it)
	if info == nil {
		panic("must register component first")
	}
	return info
}

func (c *componentMeta) GetComponentMetaInfoByType(typ reflect.Type) *ComponentMetaInfo {
	it, ok := c.types[typ]
	if !ok {
		panic(fmt.Sprintf("must register component %s first", typ.String()))
	}
	return c.infos.Get(it)
}

func (c *componentMeta) ComponentMetaInfoPrint() {
	fn := func(m map[reflect.Type]uint16) {
		total := len(m)
		count := 0
		prefix := "│  ├─"
		prefix2 := "│  └─"
		str := ""
		for typ, _ := range m {
			str += " " + typ.Name()
			count++
			if count%5 == 0 {
				if count == total {
					Log.Infof("%s%s", prefix2, str)
				} else {
					Log.Infof("%s%s", prefix, str)
				}
				str = ""
			}
		}
		if str != "" {
			Log.Infof("%s%s", prefix2, str)
			str = ""
		}
	}

	Log.Infof("┌──────────────── # Component Info # ─────────────────")
	Log.Infof("├─ Total: %d", len(c.types))
	fn(c.types)
	Log.Infof("├─ Disposable: %d", len(c.disposable))
	fn(c.disposable)
	Log.Infof("├─ Free: %d", len(c.free))
	fn(c.free)
	Log.Infof("└────────────── # Component Info End # ───────────────")
}


================================================
FILE: component_meta_test.go
================================================
package ecs

import (
	"reflect"
	"testing"
)

func BenchmarkIntTypeAndReflectType(b *testing.B) {
	infos := NewSparseArray[uint16, ComponentMetaInfo]()
	infos.Add(0, &ComponentMetaInfo{})

	m1 := map[uint16]struct{}{1: {}}
	m2 := map[reflect.Type]struct{}{reflect.TypeOf(0): {}}

	b.Run("int type", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			typ := *infos.Get(0)
			_ = m1[typ.it]
		}
	})
	b.Run("reflect type", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			typ := reflect.TypeOf(0)
			_ = m2[typ]
		}
	})
}


================================================
FILE: component_operate_task.go
================================================
package ecs

import (
	"sync"
)

type opTask struct {
	target Entity
	com    IComponent
	op     CollectionOperate
	next   *opTask
}

func (o *opTask) Reset() {
	o.target = 0
	o.com = nil
	o.op = CollectionOperateNone
	o.next = nil
}

type opTaskList struct {
	len  int
	head *opTask
	tail *opTask
}

func (o *opTaskList) Clone() *opTaskList {
	clone := *o
	return &clone
}

func (o *opTaskList) Len() int {
	return o.len
}

func (o *opTaskList) Combine(list *opTaskList) {
	if o.head == nil {
		o.head = list.head
		o.tail = list.tail
	} else {
		o.tail.next = list.head
		o.tail = list.tail
	}
	o.len += list.len
}

func (o *opTaskList) Append(task *opTask) {
	if o.head == nil {
		o.head = task
		o.tail = task
	} else {
		o.tail.next = task
		o.tail = task
	}
	o.len++
}

func (o *opTaskList) Reset() {
	o.len = 0
	o.head = nil
	o.tail = nil
}

var opTaskPool = newTaskPool()

type taskPool struct {
	pool sync.Pool
}

func newTaskPool() *taskPool {
	return &taskPool{
		pool: sync.Pool{
			New: func() interface{} {
				return new(opTask)
			},
		},
	}
}

func (p *taskPool) Get() *opTask {
	v := p.pool.Get()
	if v == nil {
		return &opTask{}
	}
	return v.(*opTask)
}

func (p *taskPool) Put(t *opTask) {
	t.Reset()
	p.pool.Put(t)
}


================================================
FILE: component_set.go
================================================
package ecs

import (
	"sort"
	"unsafe"
)

type IComponentSet interface {
	Len() int
	Range(fn func(com IComponent) bool)
	Clear()
	GetByEntity(entity Entity) any
	GetElementMeta() *ComponentMetaInfo
	GetComponent(entity Entity) IComponent
	GetComponentRaw(entity Entity) unsafe.Pointer
	Remove(entity Entity)
	Sort()

	getPointerByIndex(index int64) unsafe.Pointer
	changeCount() int64
	changeReset()
	pointer() unsafe.Pointer
	getPointerByEntity(entity Entity) unsafe.Pointer
}

type ComponentSet[T ComponentObject] struct {
	SparseArray[int32, T]
	change int64
	meta   *ComponentMetaInfo
}

func NewComponentSet[T ComponentObject](meta *ComponentMetaInfo, initSize ...int) *ComponentSet[T] {
	c := &ComponentSet[T]{
		SparseArray: *NewSparseArray[int32, T](initSize...),
		meta:        meta,
	}
	return c
}

func (c *ComponentSet[T]) Add(element *T, entity Entity) *T {
	index := entity.ToRealID().index
	data := c.SparseArray.Add(index, element)
	if data == nil {
		return nil
	}
	c.change++
	return data
}

func (c *ComponentSet[T]) remove(entity Entity) *T {
	index := entity.ToRealID().index
	return c.SparseArray.Remove(index)
}

func (c *ComponentSet[T]) Remove(entity Entity) {
	data := c.remove(entity)
	if data == nil {
		return
	}
	c.change++
}

func (c *ComponentSet[T]) RemoveAndReturn(entity Entity) *T {
	cpy := *c.remove(entity)
	return &cpy
}

func (c *ComponentSet[T]) getByEntity(entity Entity) *T {
	return c.SparseArray.Get(entity.ToRealID().index)
}

func (c *ComponentSet[T]) getPointerByEntity(entity Entity) unsafe.Pointer {
	return unsafe.Pointer(c.getByEntity(entity))
}

func (c *ComponentSet[T]) GetByEntity(entity Entity) any {
	return c.getByEntity(entity)
}

func (c *ComponentSet[T]) Get(entity Entity) *T {
	return c.getByEntity(entity)
}

func (c *ComponentSet[T]) pointer() unsafe.Pointer {
	return unsafe.Pointer(c)
}

func (c *ComponentSet[T]) changeCount() int64 {
	return c.change
}

func (c *ComponentSet[T]) changeReset() {
	c.change = 0
}

func (c *ComponentSet[T]) Sort() {
	if c.changeCount() == 0 {
		return
	}
	var zeroSeq = SeqMax
	seq2id := map[uint32]int64{}
	var cp *Component[T]
	for i := int64(0); i < int64(c.Len()); i++ {
		cp = (*Component[T])(unsafe.Pointer(&(c.data[i])))
		if cp.seq == 0 {
			zeroSeq--
			cp.seq = zeroSeq
		}
		seq2id[cp.seq] = cp.owner.ToInt64()
	}
	sort.Slice(c.data, func(i, j int) bool {
		return (*Component[T])(unsafe.Pointer(&(c.data[i]))).seq < (*Component[T])(unsafe.Pointer(&(c.data[j]))).seq
	})
	for i := int32(0); i < int32(c.Len()); i++ {
		cp = (*Component[T])(unsafe.Pointer(&(c.data[i])))
		c.indices[cp.owner.ToRealID().index] = i + 1
	}
	c.changeReset()
}

func (c *ComponentSet[T]) GetComponent(entity Entity) IComponent {
	return c.GetByEntity(entity).(IComponent)
}

func (c *ComponentSet[T]) GetComponentRaw(entity Entity) unsafe.Pointer {
	return unsafe.Pointer(c.getByEntity(entity))
}

func (c *ComponentSet[T]) getPointerByIndex(index int64) unsafe.Pointer {
	return unsafe.Pointer(c.SparseArray.UnorderedCollection.Get(index))
}

func (c *ComponentSet[T]) GetElementMeta() *ComponentMetaInfo {
	return c.meta
}

func (c *ComponentSet[T]) Range(fn func(com IComponent) bool) {
	c.SparseArray.Range(func(com *T) bool {
		return fn(any(com).(IComponent))
	})
}

func NewComponentSetIterator[T ComponentObject](collection *ComponentSet[T], readOnly ...bool) Iterator[T] {
	iter := &Iter[T]{
		data:    collection.data,
		len:     collection.Len(),
		eleSize: collection.eleSize,
		offset:  0,
	}
	if len(readOnly) > 0 {
		iter.readOnly = readOnly[0]
	}
	if iter.len != 0 {
		iter.head = unsafe.Pointer(&collection.data[0])
		if iter.readOnly {
			iter.curTemp = collection.data[0]
			iter.cur = &iter.curTemp
		} else {
			iter.cur = &(collection.data[0])
		}
	}

	return iter
}


================================================
FILE: component_set_test.go
================================================
package ecs

import (
	"math/rand"
	"testing"
)

func TestComponentSet_Sort(t *testing.T) {
	//准备数据
	caseCount := 50
	var srcList []__unorderedCollection_Test_item
	for i := 0; i < caseCount; i++ {
		srcList = append(srcList, __unorderedCollection_Test_item{
			Component: Component[__unorderedCollection_Test_item]{
				seq:   uint32(caseCount - i),
				owner: Entity(i),
			},
			ItemID: int64(i),
			Arr:    [3]int{1, 2, 3},
		})
	}

	//创建容器(无序数据集)
	c := NewComponentSet[__unorderedCollection_Test_item](&ComponentMetaInfo{})

	//添加数据
	for i := 0; i < caseCount; i++ {
		_ = c.Add(&srcList[i], srcList[i].Owner())
	}

	i := 0
	c.Range(func(item IComponent) bool {
		if item.(*__unorderedCollection_Test_item).seq != uint32(caseCount-i) {
			t.Errorf("sort error, want %d, got %d", caseCount, item.(*__unorderedCollection_Test_item).seq)
			return false
		}
		i++
		return true
	})

	//排序
	c.Sort()

	//验证
	i = 1
	c.Range(func(item IComponent) bool {
		if item.(*__unorderedCollection_Test_item).seq != uint32(i) {
			t.Errorf("sort error, want %d, got %d", i, item.(*__unorderedCollection_Test_item).seq)
			return false
		}
		i++
		return true
	})
}

func TestNewComponentSet(t *testing.T) {
	cs := NewComponentSet[__unorderedCollection_Test_item](&ComponentMetaInfo{})
	if cs.GetElementMeta().it != 0 {
		t.Error("element meta error")
	}
}

func BenchmarkComponentSet_Read(b *testing.B) {
	c := NewComponentSet[__unorderedCollection_Test_item](&ComponentMetaInfo{})
	var ids []int64
	total := 1000000
	for n := 0; n < total; n++ {
		item := &__unorderedCollection_Test_item{
			ItemID: int64(n),
		}
		_ = c.Add(item, Entity(n))
		ids = append(ids, int64(n+1))
	}

	seq := make([]int, total)
	r := make([]int, total)

	for i := 0; i < total; i++ {
		seq[i] = i
		r[i] = i
	}
	rand.Shuffle(len(r), func(i, j int) {
		r[i], r[j] = r[j], r[i]
	})

	b.ResetTimer()

	b.Run("sequence", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = c.Get(Entity(seq[n%total]))
		}
	})
	b.Run("random", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = c.Get(Entity(r[n%total]))
		}
	})
}


================================================
FILE: component_test.go
================================================
package ecs

import "testing"

func TestComponent_isValidComponentType(t *testing.T) {
	type C1 struct {
		Component[C1]
		Field1 int
		Field2 struct {
			Field1 int
		}
	}

	type C2 struct {
		Component[C2]
		Field1 string
	}

	type C3 struct {
		Component[C3]
		Field1 *int
	}

	type C4 struct {
		Component[C4]
		Field1 int
		Field2 struct {
			Field1 *int
		}
	}

	type C5 struct {
		Component[C5]
		Field1 int
		Field2 struct {
			Field1 struct {
				Field1 int
			}
			Field2 uint32
		}
		Field3 FixedString[Fixed5]
	}

	tests := []struct {
		name string
		c    IComponent
		want bool
	}{
		{
			name: "Test1",
			c:    &C1{},
			want: true,
		},
		{
			name: "Test2",
			c:    &C2{},
			want: false,
		},
		{
			name: "Test3",
			c:    &C3{},
			want: false,
		},
		{
			name: "Test4",
			c:    &C4{},
			want: false,
		},
		{
			name: "Test5",
			c:    &C5{},
			want: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.c.isValidComponentType(); got != tt.want {
				t.Errorf("isValidComponentType() = %v, want %v", got, tt.want)
			}
		})
	}
}


================================================
FILE: component_utils.go
================================================
package ecs

import (
	"encoding/json"
	"reflect"
)

func GetType[T ComponentObject]() reflect.Type {
	return TypeOf[T]()
}

func ObjectToString(in interface{}) string {
	b, err := json.Marshal(in)
	if err != nil {
		return err.Error()
	}
	return string(b)
}


================================================
FILE: compound.go
================================================
package ecs

type Compound = OrderedIntSet[uint16]

func NewCompound(initCap ...int) Compound {
	cap := 0
	if len(initCap) > 0 {
		cap = initCap[0]
	}
	return make(Compound, 0, cap)
}


================================================
FILE: compound_test.go
================================================
package ecs

import (
	"bufio"
	"fmt"
	"os"
	"testing"
)

func Test_getCompoundType(t *testing.T) {
	return
	filePath := "./compound_utils.go"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败", err)
	}
	defer file.Close()
	write := bufio.NewWriter(file)
	h1 := `
package ecs

import (
	"unsafe"
)

func getCompoundType(compound Compound) interface{} {
	length := len(compound)
	if length == 0 || length > 255 {
		return nil
	}
	switch length {`
	write.WriteString(h1)
	h2 := `
	case %d:
		return *(*[%d]uint16)(unsafe.Pointer(&compound[0]))`
	for i := 1; i < 256; i++ {
		write.WriteString(fmt.Sprintf(h2, i, i))
	}

	h3 := `
	}

	return nil
}`
	write.WriteString(h3)
	write.Flush()
}

func TestCompound_find(t *testing.T) {
	type args struct {
		it uint16
	}
	tests := []struct {
		name string
		c    Compound
		args args
		want int
	}{
		{
			name: "1",
			c:    Compound{1, 3, 5},
			args: args{it: 3},
			want: 1,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.c.Find(tt.args.it); got != tt.want {
				t.Errorf("Find() = %v, want %v", got, tt.want)
			}
		})
	}

}

func TestCompound_insertIndex(t *testing.T) {
	type args struct {
		it uint16
	}
	tests := []struct {
		name string
		c    Compound
		args args
		want int
	}{
		{
			name: "1",
			c:    Compound{1, 3, 4, 6, 7},
			args: args{it: 5},
			want: 3,
		},
		{
			name: "2",
			c:    Compound{1, 3, 4, 6, 9, 10},
			args: args{it: 5},
			want: 3,
		},
		{
			name: "3",
			c:    Compound{2, 3, 5, 5, 6},
			args: args{it: 1},
			want: 0,
		},
		{
			name: "4",
			c:    Compound{1, 2, 3, 5, 7, 8},
			args: args{it: 6},
			want: 4,
		},
		{
			name: "5",
			c:    Compound{1, 2, 3, 4, 5, 6},
			args: args{it: 7},
			want: 6,
		},
		{
			name: "6",
			c:    Compound{1, 2, 3, 4, 5, 6},
			args: args{it: 3},
			want: -1,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.c.InsertIndex(tt.args.it); got != tt.want {
				t.Errorf("insertIndex() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCompound_Add(t *testing.T) {
	type args struct {
		it uint16
	}
	tests := []struct {
		name    string
		c       Compound
		args    args
		wantErr bool
	}{
		{
			name:    "1",
			c:       Compound{1, 2, 4, 5, 6},
			args:    args{it: 3},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if ok := tt.c.Add(tt.args.it); (ok != true) != tt.wantErr {
				t.Errorf("Add() error = %v, wantErr %v", ok, tt.wantErr)
			}
		})
	}
}

func TestCompound_Remove(t *testing.T) {
	type args struct {
		it uint16
	}
	tests := []struct {
		name string
		c    Compound
		args args
	}{
		{
			name: "1",
			c:    Compound{1, 2, 3, 4, 5, 6},
			args: args{it: 3},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.c.Remove(tt.args.it)
		})
	}
}

func BenchmarkCompound_Add(b *testing.B) {
	c := Compound{}
	for i := 0; i < b.N; i++ {
		c.Add(uint16(i % 65535))
	}
}

const (
	CompoundSize = 20
)

func BenchmarkCompound_Find(b *testing.B) {
	c := Compound{}
	for i := 0; i < CompoundSize; i++ {
		c.Add(uint16(i % CompoundSize))
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c.Find(uint16(i % CompoundSize))
	}
}

func BenchmarkCompound_MapFind(b *testing.B) {
	m := map[uint16]struct{}{}
	for i := 0; i < CompoundSize; i++ {
		m[(uint16(i % CompoundSize))] = struct{}{}
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, ok := m[(uint16(i % CompoundSize))]
		_ = ok
	}
}

func BenchmarkCompound_BigMapFind(b *testing.B) {
	m := map[int]struct{}{}
	for i := 0; i < 50000; i++ {
		m[i] = struct{}{}
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, ok := m[i%50000]
		_ = ok
	}
}


================================================
FILE: compound_utils.go
================================================
package ecs

import (
	"unsafe"
)

func getCompoundType(compound Compound) interface{} {
	length := len(compound)
	if length == 0 || length > 255 {
		return nil
	}
	switch length {
	case 1:
		return *(*[1]uint16)(unsafe.Pointer(&compound[0]))
	case 2:
		return *(*[2]uint16)(unsafe.Pointer(&compound[0]))
	case 3:
		return *(*[3]uint16)(unsafe.Pointer(&compound[0]))
	case 4:
		return *(*[4]uint16)(unsafe.Pointer(&compound[0]))
	case 5:
		return *(*[5]uint16)(unsafe.Pointer(&compound[0]))
	case 6:
		return *(*[6]uint16)(unsafe.Pointer(&compound[0]))
	case 7:
		return *(*[7]uint16)(unsafe.Pointer(&compound[0]))
	case 8:
		return *(*[8]uint16)(unsafe.Pointer(&compound[0]))
	case 9:
		return *(*[9]uint16)(unsafe.Pointer(&compound[0]))
	case 10:
		return *(*[10]uint16)(unsafe.Pointer(&compound[0]))
	case 11:
		return *(*[11]uint16)(unsafe.Pointer(&compound[0]))
	case 12:
		return *(*[12]uint16)(unsafe.Pointer(&compound[0]))
	case 13:
		return *(*[13]uint16)(unsafe.Pointer(&compound[0]))
	case 14:
		return *(*[14]uint16)(unsafe.Pointer(&compound[0]))
	case 15:
		return *(*[15]uint16)(unsafe.Pointer(&compound[0]))
	case 16:
		return *(*[16]uint16)(unsafe.Pointer(&compound[0]))
	case 17:
		return *(*[17]uint16)(unsafe.Pointer(&compound[0]))
	case 18:
		return *(*[18]uint16)(unsafe.Pointer(&compound[0]))
	case 19:
		return *(*[19]uint16)(unsafe.Pointer(&compound[0]))
	case 20:
		return *(*[20]uint16)(unsafe.Pointer(&compound[0]))
	case 21:
		return *(*[21]uint16)(unsafe.Pointer(&compound[0]))
	case 22:
		return *(*[22]uint16)(unsafe.Pointer(&compound[0]))
	case 23:
		return *(*[23]uint16)(unsafe.Pointer(&compound[0]))
	case 24:
		return *(*[24]uint16)(unsafe.Pointer(&compound[0]))
	case 25:
		return *(*[25]uint16)(unsafe.Pointer(&compound[0]))
	case 26:
		return *(*[26]uint16)(unsafe.Pointer(&compound[0]))
	case 27:
		return *(*[27]uint16)(unsafe.Pointer(&compound[0]))
	case 28:
		return *(*[28]uint16)(unsafe.Pointer(&compound[0]))
	case 29:
		return *(*[29]uint16)(unsafe.Pointer(&compound[0]))
	case 30:
		return *(*[30]uint16)(unsafe.Pointer(&compound[0]))
	case 31:
		return *(*[31]uint16)(unsafe.Pointer(&compound[0]))
	case 32:
		return *(*[32]uint16)(unsafe.Pointer(&compound[0]))
	case 33:
		return *(*[33]uint16)(unsafe.Pointer(&compound[0]))
	case 34:
		return *(*[34]uint16)(unsafe.Pointer(&compound[0]))
	case 35:
		return *(*[35]uint16)(unsafe.Pointer(&compound[0]))
	case 36:
		return *(*[36]uint16)(unsafe.Pointer(&compound[0]))
	case 37:
		return *(*[37]uint16)(unsafe.Pointer(&compound[0]))
	case 38:
		return *(*[38]uint16)(unsafe.Pointer(&compound[0]))
	case 39:
		return *(*[39]uint16)(unsafe.Pointer(&compound[0]))
	case 40:
		return *(*[40]uint16)(unsafe.Pointer(&compound[0]))
	case 41:
		return *(*[41]uint16)(unsafe.Pointer(&compound[0]))
	case 42:
		return *(*[42]uint16)(unsafe.Pointer(&compound[0]))
	case 43:
		return *(*[43]uint16)(unsafe.Pointer(&compound[0]))
	case 44:
		return *(*[44]uint16)(unsafe.Pointer(&compound[0]))
	case 45:
		return *(*[45]uint16)(unsafe.Pointer(&compound[0]))
	case 46:
		return *(*[46]uint16)(unsafe.Pointer(&compound[0]))
	case 47:
		return *(*[47]uint16)(unsafe.Pointer(&compound[0]))
	case 48:
		return *(*[48]uint16)(unsafe.Pointer(&compound[0]))
	case 49:
		return *(*[49]uint16)(unsafe.Pointer(&compound[0]))
	case 50:
		return *(*[50]uint16)(unsafe.Pointer(&compound[0]))
	case 51:
		return *(*[51]uint16)(unsafe.Pointer(&compound[0]))
	case 52:
		return *(*[52]uint16)(unsafe.Pointer(&compound[0]))
	case 53:
		return *(*[53]uint16)(unsafe.Pointer(&compound[0]))
	case 54:
		return *(*[54]uint16)(unsafe.Pointer(&compound[0]))
	case 55:
		return *(*[55]uint16)(unsafe.Pointer(&compound[0]))
	case 56:
		return *(*[56]uint16)(unsafe.Pointer(&compound[0]))
	case 57:
		return *(*[57]uint16)(unsafe.Pointer(&compound[0]))
	case 58:
		return *(*[58]uint16)(unsafe.Pointer(&compound[0]))
	case 59:
		return *(*[59]uint16)(unsafe.Pointer(&compound[0]))
	case 60:
		return *(*[60]uint16)(unsafe.Pointer(&compound[0]))
	case 61:
		return *(*[61]uint16)(unsafe.Pointer(&compound[0]))
	case 62:
		return *(*[62]uint16)(unsafe.Pointer(&compound[0]))
	case 63:
		return *(*[63]uint16)(unsafe.Pointer(&compound[0]))
	case 64:
		return *(*[64]uint16)(unsafe.Pointer(&compound[0]))
	case 65:
		return *(*[65]uint16)(unsafe.Pointer(&compound[0]))
	case 66:
		return *(*[66]uint16)(unsafe.Pointer(&compound[0]))
	case 67:
		return *(*[67]uint16)(unsafe.Pointer(&compound[0]))
	case 68:
		return *(*[68]uint16)(unsafe.Pointer(&compound[0]))
	case 69:
		return *(*[69]uint16)(unsafe.Pointer(&compound[0]))
	case 70:
		return *(*[70]uint16)(unsafe.Pointer(&compound[0]))
	case 71:
		return *(*[71]uint16)(unsafe.Pointer(&compound[0]))
	case 72:
		return *(*[72]uint16)(unsafe.Pointer(&compound[0]))
	case 73:
		return *(*[73]uint16)(unsafe.Pointer(&compound[0]))
	case 74:
		return *(*[74]uint16)(unsafe.Pointer(&compound[0]))
	case 75:
		return *(*[75]uint16)(unsafe.Pointer(&compound[0]))
	case 76:
		return *(*[76]uint16)(unsafe.Pointer(&compound[0]))
	case 77:
		return *(*[77]uint16)(unsafe.Pointer(&compound[0]))
	case 78:
		return *(*[78]uint16)(unsafe.Pointer(&compound[0]))
	case 79:
		return *(*[79]uint16)(unsafe.Pointer(&compound[0]))
	case 80:
		return *(*[80]uint16)(unsafe.Pointer(&compound[0]))
	case 81:
		return *(*[81]uint16)(unsafe.Pointer(&compound[0]))
	case 82:
		return *(*[82]uint16)(unsafe.Pointer(&compound[0]))
	case 83:
		return *(*[83]uint16)(unsafe.Pointer(&compound[0]))
	case 84:
		return *(*[84]uint16)(unsafe.Pointer(&compound[0]))
	case 85:
		return *(*[85]uint16)(unsafe.Pointer(&compound[0]))
	case 86:
		return *(*[86]uint16)(unsafe.Pointer(&compound[0]))
	case 87:
		return *(*[87]uint16)(unsafe.Pointer(&compound[0]))
	case 88:
		return *(*[88]uint16)(unsafe.Pointer(&compound[0]))
	case 89:
		return *(*[89]uint16)(unsafe.Pointer(&compound[0]))
	case 90:
		return *(*[90]uint16)(unsafe.Pointer(&compound[0]))
	case 91:
		return *(*[91]uint16)(unsafe.Pointer(&compound[0]))
	case 92:
		return *(*[92]uint16)(unsafe.Pointer(&compound[0]))
	case 93:
		return *(*[93]uint16)(unsafe.Pointer(&compound[0]))
	case 94:
		return *(*[94]uint16)(unsafe.Pointer(&compound[0]))
	case 95:
		return *(*[95]uint16)(unsafe.Pointer(&compound[0]))
	case 96:
		return *(*[96]uint16)(unsafe.Pointer(&compound[0]))
	case 97:
		return *(*[97]uint16)(unsafe.Pointer(&compound[0]))
	case 98:
		return *(*[98]uint16)(unsafe.Pointer(&compound[0]))
	case 99:
		return *(*[99]uint16)(unsafe.Pointer(&compound[0]))
	case 100:
		return *(*[100]uint16)(unsafe.Pointer(&compound[0]))
	case 101:
		return *(*[101]uint16)(unsafe.Pointer(&compound[0]))
	case 102:
		return *(*[102]uint16)(unsafe.Pointer(&compound[0]))
	case 103:
		return *(*[103]uint16)(unsafe.Pointer(&compound[0]))
	case 104:
		return *(*[104]uint16)(unsafe.Pointer(&compound[0]))
	case 105:
		return *(*[105]uint16)(unsafe.Pointer(&compound[0]))
	case 106:
		return *(*[106]uint16)(unsafe.Pointer(&compound[0]))
	case 107:
		return *(*[107]uint16)(unsafe.Pointer(&compound[0]))
	case 108:
		return *(*[108]uint16)(unsafe.Pointer(&compound[0]))
	case 109:
		return *(*[109]uint16)(unsafe.Pointer(&compound[0]))
	case 110:
		return *(*[110]uint16)(unsafe.Pointer(&compound[0]))
	case 111:
		return *(*[111]uint16)(unsafe.Pointer(&compound[0]))
	case 112:
		return *(*[112]uint16)(unsafe.Pointer(&compound[0]))
	case 113:
		return *(*[113]uint16)(unsafe.Pointer(&compound[0]))
	case 114:
		return *(*[114]uint16)(unsafe.Pointer(&compound[0]))
	case 115:
		return *(*[115]uint16)(unsafe.Pointer(&compound[0]))
	case 116:
		return *(*[116]uint16)(unsafe.Pointer(&compound[0]))
	case 117:
		return *(*[117]uint16)(unsafe.Pointer(&compound[0]))
	case 118:
		return *(*[118]uint16)(unsafe.Pointer(&compound[0]))
	case 119:
		return *(*[119]uint16)(unsafe.Pointer(&compound[0]))
	case 120:
		return *(*[120]uint16)(unsafe.Pointer(&compound[0]))
	case 121:
		return *(*[121]uint16)(unsafe.Pointer(&compound[0]))
	case 122:
		return *(*[122]uint16)(unsafe.Pointer(&compound[0]))
	case 123:
		return *(*[123]uint16)(unsafe.Pointer(&compound[0]))
	case 124:
		return *(*[124]uint16)(unsafe.Pointer(&compound[0]))
	case 125:
		return *(*[125]uint16)(unsafe.Pointer(&compound[0]))
	case 126:
		return *(*[126]uint16)(unsafe.Pointer(&compound[0]))
	case 127:
		return *(*[127]uint16)(unsafe.Pointer(&compound[0]))
	case 128:
		return *(*[128]uint16)(unsafe.Pointer(&compound[0]))
	case 129:
		return *(*[129]uint16)(unsafe.Pointer(&compound[0]))
	case 130:
		return *(*[130]uint16)(unsafe.Pointer(&compound[0]))
	case 131:
		return *(*[131]uint16)(unsafe.Pointer(&compound[0]))
	case 132:
		return *(*[132]uint16)(unsafe.Pointer(&compound[0]))
	case 133:
		return *(*[133]uint16)(unsafe.Pointer(&compound[0]))
	case 134:
		return *(*[134]uint16)(unsafe.Pointer(&compound[0]))
	case 135:
		return *(*[135]uint16)(unsafe.Pointer(&compound[0]))
	case 136:
		return *(*[136]uint16)(unsafe.Pointer(&compound[0]))
	case 137:
		return *(*[137]uint16)(unsafe.Pointer(&compound[0]))
	case 138:
		return *(*[138]uint16)(unsafe.Pointer(&compound[0]))
	case 139:
		return *(*[139]uint16)(unsafe.Pointer(&compound[0]))
	case 140:
		return *(*[140]uint16)(unsafe.Pointer(&compound[0]))
	case 141:
		return *(*[141]uint16)(unsafe.Pointer(&compound[0]))
	case 142:
		return *(*[142]uint16)(unsafe.Pointer(&compound[0]))
	case 143:
		return *(*[143]uint16)(unsafe.Pointer(&compound[0]))
	case 144:
		return *(*[144]uint16)(unsafe.Pointer(&compound[0]))
	case 145:
		return *(*[145]uint16)(unsafe.Pointer(&compound[0]))
	case 146:
		return *(*[146]uint16)(unsafe.Pointer(&compound[0]))
	case 147:
		return *(*[147]uint16)(unsafe.Pointer(&compound[0]))
	case 148:
		return *(*[148]uint16)(unsafe.Pointer(&compound[0]))
	case 149:
		return *(*[149]uint16)(unsafe.Pointer(&compound[0]))
	case 150:
		return *(*[150]uint16)(unsafe.Pointer(&compound[0]))
	case 151:
		return *(*[151]uint16)(unsafe.Pointer(&compound[0]))
	case 152:
		return *(*[152]uint16)(unsafe.Pointer(&compound[0]))
	case 153:
		return *(*[153]uint16)(unsafe.Pointer(&compound[0]))
	case 154:
		return *(*[154]uint16)(unsafe.Pointer(&compound[0]))
	case 155:
		return *(*[155]uint16)(unsafe.Pointer(&compound[0]))
	case 156:
		return *(*[156]uint16)(unsafe.Pointer(&compound[0]))
	case 157:
		return *(*[157]uint16)(unsafe.Pointer(&compound[0]))
	case 158:
		return *(*[158]uint16)(unsafe.Pointer(&compound[0]))
	case 159:
		return *(*[159]uint16)(unsafe.Pointer(&compound[0]))
	case 160:
		return *(*[160]uint16)(unsafe.Pointer(&compound[0]))
	case 161:
		return *(*[161]uint16)(unsafe.Pointer(&compound[0]))
	case 162:
		return *(*[162]uint16)(unsafe.Pointer(&compound[0]))
	case 163:
		return *(*[163]uint16)(unsafe.Pointer(&compound[0]))
	case 164:
		return *(*[164]uint16)(unsafe.Pointer(&compound[0]))
	case 165:
		return *(*[165]uint16)(unsafe.Pointer(&compound[0]))
	case 166:
		return *(*[166]uint16)(unsafe.Pointer(&compound[0]))
	case 167:
		return *(*[167]uint16)(unsafe.Pointer(&compound[0]))
	case 168:
		return *(*[168]uint16)(unsafe.Pointer(&compound[0]))
	case 169:
		return *(*[169]uint16)(unsafe.Pointer(&compound[0]))
	case 170:
		return *(*[170]uint16)(unsafe.Pointer(&compound[0]))
	case 171:
		return *(*[171]uint16)(unsafe.Pointer(&compound[0]))
	case 172:
		return *(*[172]uint16)(unsafe.Pointer(&compound[0]))
	case 173:
		return *(*[173]uint16)(unsafe.Pointer(&compound[0]))
	case 174:
		return *(*[174]uint16)(unsafe.Pointer(&compound[0]))
	case 175:
		return *(*[175]uint16)(unsafe.Pointer(&compound[0]))
	case 176:
		return *(*[176]uint16)(unsafe.Pointer(&compound[0]))
	case 177:
		return *(*[177]uint16)(unsafe.Pointer(&compound[0]))
	case 178:
		return *(*[178]uint16)(unsafe.Pointer(&compound[0]))
	case 179:
		return *(*[179]uint16)(unsafe.Pointer(&compound[0]))
	case 180:
		return *(*[180]uint16)(unsafe.Pointer(&compound[0]))
	case 181:
		return *(*[181]uint16)(unsafe.Pointer(&compound[0]))
	case 182:
		return *(*[182]uint16)(unsafe.Pointer(&compound[0]))
	case 183:
		return *(*[183]uint16)(unsafe.Pointer(&compound[0]))
	case 184:
		return *(*[184]uint16)(unsafe.Pointer(&compound[0]))
	case 185:
		return *(*[185]uint16)(unsafe.Pointer(&compound[0]))
	case 186:
		return *(*[186]uint16)(unsafe.Pointer(&compound[0]))
	case 187:
		return *(*[187]uint16)(unsafe.Pointer(&compound[0]))
	case 188:
		return *(*[188]uint16)(unsafe.Pointer(&compound[0]))
	case 189:
		return *(*[189]uint16)(unsafe.Pointer(&compound[0]))
	case 190:
		return *(*[190]uint16)(unsafe.Pointer(&compound[0]))
	case 191:
		return *(*[191]uint16)(unsafe.Pointer(&compound[0]))
	case 192:
		return *(*[192]uint16)(unsafe.Pointer(&compound[0]))
	case 193:
		return *(*[193]uint16)(unsafe.Pointer(&compound[0]))
	case 194:
		return *(*[194]uint16)(unsafe.Pointer(&compound[0]))
	case 195:
		return *(*[195]uint16)(unsafe.Pointer(&compound[0]))
	case 196:
		return *(*[196]uint16)(unsafe.Pointer(&compound[0]))
	case 197:
		return *(*[197]uint16)(unsafe.Pointer(&compound[0]))
	case 198:
		return *(*[198]uint16)(unsafe.Pointer(&compound[0]))
	case 199:
		return *(*[199]uint16)(unsafe.Pointer(&compound[0]))
	case 200:
		return *(*[200]uint16)(unsafe.Pointer(&compound[0]))
	case 201:
		return *(*[201]uint16)(unsafe.Pointer(&compound[0]))
	case 202:
		return *(*[202]uint16)(unsafe.Pointer(&compound[0]))
	case 203:
		return *(*[203]uint16)(unsafe.Pointer(&compound[0]))
	case 204:
		return *(*[204]uint16)(unsafe.Pointer(&compound[0]))
	case 205:
		return *(*[205]uint16)(unsafe.Pointer(&compound[0]))
	case 206:
		return *(*[206]uint16)(unsafe.Pointer(&compound[0]))
	case 207:
		return *(*[207]uint16)(unsafe.Pointer(&compound[0]))
	case 208:
		return *(*[208]uint16)(unsafe.Pointer(&compound[0]))
	case 209:
		return *(*[209]uint16)(unsafe.Pointer(&compound[0]))
	case 210:
		return *(*[210]uint16)(unsafe.Pointer(&compound[0]))
	case 211:
		return *(*[211]uint16)(unsafe.Pointer(&compound[0]))
	case 212:
		return *(*[212]uint16)(unsafe.Pointer(&compound[0]))
	case 213:
		return *(*[213]uint16)(unsafe.Pointer(&compound[0]))
	case 214:
		return *(*[214]uint16)(unsafe.Pointer(&compound[0]))
	case 215:
		return *(*[215]uint16)(unsafe.Pointer(&compound[0]))
	case 216:
		return *(*[216]uint16)(unsafe.Pointer(&compound[0]))
	case 217:
		return *(*[217]uint16)(unsafe.Pointer(&compound[0]))
	case 218:
		return *(*[218]uint16)(unsafe.Pointer(&compound[0]))
	case 219:
		return *(*[219]uint16)(unsafe.Pointer(&compound[0]))
	case 220:
		return *(*[220]uint16)(unsafe.Pointer(&compound[0]))
	case 221:
		return *(*[221]uint16)(unsafe.Pointer(&compound[0]))
	case 222:
		return *(*[222]uint16)(unsafe.Pointer(&compound[0]))
	case 223:
		return *(*[223]uint16)(unsafe.Pointer(&compound[0]))
	case 224:
		return *(*[224]uint16)(unsafe.Pointer(&compound[0]))
	case 225:
		return *(*[225]uint16)(unsafe.Pointer(&compound[0]))
	case 226:
		return *(*[226]uint16)(unsafe.Pointer(&compound[0]))
	case 227:
		return *(*[227]uint16)(unsafe.Pointer(&compound[0]))
	case 228:
		return *(*[228]uint16)(unsafe.Pointer(&compound[0]))
	case 229:
		return *(*[229]uint16)(unsafe.Pointer(&compound[0]))
	case 230:
		return *(*[230]uint16)(unsafe.Pointer(&compound[0]))
	case 231:
		return *(*[231]uint16)(unsafe.Pointer(&compound[0]))
	case 232:
		return *(*[232]uint16)(unsafe.Pointer(&compound[0]))
	case 233:
		return *(*[233]uint16)(unsafe.Pointer(&compound[0]))
	case 234:
		return *(*[234]uint16)(unsafe.Pointer(&compound[0]))
	case 235:
		return *(*[235]uint16)(unsafe.Pointer(&compound[0]))
	case 236:
		return *(*[236]uint16)(unsafe.Pointer(&compound[0]))
	case 237:
		return *(*[237]uint16)(unsafe.Pointer(&compound[0]))
	case 238:
		return *(*[238]uint16)(unsafe.Pointer(&compound[0]))
	case 239:
		return *(*[239]uint16)(unsafe.Pointer(&compound[0]))
	case 240:
		return *(*[240]uint16)(unsafe.Pointer(&compound[0]))
	case 241:
		return *(*[241]uint16)(unsafe.Pointer(&compound[0]))
	case 242:
		return *(*[242]uint16)(unsafe.Pointer(&compound[0]))
	case 243:
		return *(*[243]uint16)(unsafe.Pointer(&compound[0]))
	case 244:
		return *(*[244]uint16)(unsafe.Pointer(&compound[0]))
	case 245:
		return *(*[245]uint16)(unsafe.Pointer(&compound[0]))
	case 246:
		return *(*[246]uint16)(unsafe.Pointer(&compound[0]))
	case 247:
		return *(*[247]uint16)(unsafe.Pointer(&compound[0]))
	case 248:
		return *(*[248]uint16)(unsafe.Pointer(&compound[0]))
	case 249:
		return *(*[249]uint16)(unsafe.Pointer(&compound[0]))
	case 250:
		return *(*[250]uint16)(unsafe.Pointer(&compound[0]))
	case 251:
		return *(*[251]uint16)(unsafe.Pointer(&compound[0]))
	case 252:
		return *(*[252]uint16)(unsafe.Pointer(&compound[0]))
	case 253:
		return *(*[253]uint16)(unsafe.Pointer(&compound[0]))
	case 254:
		return *(*[254]uint16)(unsafe.Pointer(&compound[0]))
	case 255:
		return *(*[255]uint16)(unsafe.Pointer(&compound[0]))
	}

	return nil
}


================================================
FILE: concurrent_map.go
================================================
package ecs

import (
	"sync"
	"sync/atomic"
	"unsafe"
)

type Map[K comparable, V any] struct {
	mu     sync.Mutex
	read   atomic.Value
	dirty  map[K]*entry[V]
	misses int
}

type readOnly[K comparable, V any] struct {
	m       map[K]*entry[V]
	amended bool
}

var expunged = unsafe.Pointer(new(interface{}))

type entry[V any] struct {
	p unsafe.Pointer
}

func newEntry[V any](i V) *entry[V] {
	return &entry[V]{p: unsafe.Pointer(&i)}
}

func (m *Map[K, V]) Load(key K) (value V, ok bool) {
	read, _ := m.read.Load().(readOnly[K, V])
	e, ok := read.m[key]
	if !ok && read.amended {
		m.mu.Lock()
		read, _ = m.read.Load().(readOnly[K, V])
		e, ok = read.m[key]
		if !ok && read.amended {
			e, ok = m.dirty[key]
			m.missLocked()
		}
		m.mu.Unlock()
	}
	if !ok {
		return *new(V), false
	}
	return e.load()
}

func (e *entry[V]) load() (value V, ok bool) {
	p := atomic.LoadPointer(&e.p)
	if p == nil || p == expunged {
		return *new(V), false
	}
	return *(*V)(p), true
}

func (m *Map[K, V]) Store(key K, value V) {
	read, _ := m.read.Load().(readOnly[K, V])
	if e, ok := read.m[key]; ok && e.tryStore(&value) {
		return
	}

	m.mu.Lock()
	read, _ = m.read.Load().(readOnly[K, V])
	if e, ok := read.m[key]; ok {
		if e.unexpungeLocked() {
			m.dirty[key] = e
		}
		e.storeLocked(&value)
	} else if e, ok := m.dirty[key]; ok {
		e.storeLocked(&value)
	} else {
		if !read.amended {
			m.dirtyLocked()
			m.read.Store(readOnly[K, V]{m: read.m, amended: true})
		}
		m.dirty[key] = newEntry[V](value)
	}
	m.mu.Unlock()
}

func (e *entry[V]) tryStore(i *V) bool {
	for {
		p := atomic.LoadPointer(&e.p)
		if p == expunged {
			return false
		}
		if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
			return true
		}
	}
}

func (e *entry[V]) unexpungeLocked() (wasExpunged bool) {
	return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
}

func (e *entry[V]) storeLocked(i *V) {
	atomic.StorePointer(&e.p, unsafe.Pointer(i))
}

func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
	read, _ := m.read.Load().(readOnly[K, V])
	if e, ok := read.m[key]; ok {
		actual, loaded, ok := e.tryLoadOrStore(value)
		if ok {
			return actual, loaded
		}
	}

	m.mu.Lock()
	read, _ = m.read.Load().(readOnly[K, V])
	if e, ok := read.m[key]; ok {
		if e.unexpungeLocked() {
			m.dirty[key] = e
		}
		actual, loaded, _ = e.tryLoadOrStore(value)
	} else if e, ok := m.dirty[key]; ok {
		actual, loaded, _ = e.tryLoadOrStore(value)
		m.missLocked()
	} else {
		if !read.amended {
			m.dirtyLocked()
			m.read.Store(readOnly[K, V]{m: read.m, amended: true})
		}
		m.dirty[key] = newEntry(value)
		actual, loaded = value, false
	}
	m.mu.Unlock()

	return actual, loaded
}

func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) {
	p := atomic.LoadPointer(&e.p)
	if p == expunged {
		return *new(V), false, false
	}
	if p != nil {
		return *(*V)(p), true, true
	}

	ic := i
	for {
		if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
			return i, false, true
		}
		p = atomic.LoadPointer(&e.p)
		if p == expunged {
			return *new(V), false, false
		}
		if p != nil {
			return *(*V)(p), true, true
		}
	}
}

func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
	read, _ := m.read.Load().(readOnly[K, V])
	e, ok := read.m[key]
	if !ok && read.amended {
		m.mu.Lock()
		read, _ = m.read.Load().(readOnly[K, V])
		e, ok = read.m[key]
		if !ok && read.amended {
			e, ok = m.dirty[key]
			delete(m.dirty, key)
			m.missLocked()
		}
		m.mu.Unlock()
	}
	if ok {
		return e.delete()
	}
	return *new(V), false
}

func (m *Map[K, V]) Delete(key K) {
	m.LoadAndDelete(key)
}

func (e *entry[V]) delete() (value V, ok bool) {
	for {
		p := atomic.LoadPointer(&e.p)
		if p == nil || p == expunged {
			return *new(V), false
		}
		if atomic.CompareAndSwapPointer(&e.p, p, nil) {
			return *(*V)(p), true
		}
	}
}

func (m *Map[K, V]) Range(f func(key K, value V) bool) bool {
	read, _ := m.read.Load().(readOnly[K, V])
	if read.amended {
		m.mu.Lock()
		read, _ = m.read.Load().(readOnly[K, V])
		if read.amended {
			read = readOnly[K, V]{m: m.dirty}
			m.read.Store(read)
			m.dirty = nil
			m.misses = 0
		}
		m.mu.Unlock()
	}

	for k, e := range read.m {
		v, ok := e.load()
		if !ok {
			continue
		}
		if !f(k, v) {
			return false
		}
	}
	return true
}

func (m *Map[K, V]) missLocked() {
	m.misses++
	if m.misses < len(m.dirty) {
		return
	}
	m.read.Store(readOnly[K, V]{m: m.dirty})
	m.dirty = nil
	m.misses = 0
}

func (m *Map[K, V]) dirtyLocked() {
	if m.dirty != nil {
		return
	}

	read, _ := m.read.Load().(readOnly[K, V])
	m.dirty = make(map[K]*entry[V], len(read.m))
	for k, e := range read.m {
		if !e.tryExpungeLocked() {
			m.dirty[k] = e
		}
	}
}

func (e *entry[V]) tryExpungeLocked() (isExpunged bool) {
	p := atomic.LoadPointer(&e.p)
	for p == nil {
		if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
			return true
		}
		p = atomic.LoadPointer(&e.p)
	}
	return p == expunged
}


================================================
FILE: concurrent_map_test.go
================================================
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ecs_test

import (
	"github.com/zllangct/ecs"
	"math/rand"
	"runtime"
	"sync"
	"testing"
)

func TestConcurrentRange(t *testing.T) {
	const mapSize = 1 << 10

	m := new(ecs.Map[int64, int64])
	for n := int64(1); n <= mapSize; n++ {
		m.Store(n, int64(n))
	}

	done := make(chan struct{})
	var wg sync.WaitGroup
	defer func() {
		close(done)
		wg.Wait()
	}()
	for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- {
		r := rand.New(rand.NewSource(g))
		wg.Add(1)
		go func(g int64) {
			defer wg.Done()
			for i := int64(0); ; i++ {
				select {
				case <-done:
					return
				default:
				}
				for n := int64(1); n < mapSize; n++ {
					if r.Int63n(mapSize) == 0 {
						m.Store(n, n*i*g)
					} else {
						m.Load(n)
					}
				}
			}
		}(g)
	}

	iters := 1 << 10
	if testing.Short() {
		iters = 16
	}
	for n := iters; n > 0; n-- {
		seen := make(map[int64]bool, mapSize)

		m.Range(func(ki int64, vi int64) bool {
			k, v := ki, vi
			if v%k != 0 {
				t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v)
			}
			if seen[k] {
				t.Fatalf("Range visited key %v twice", k)
			}
			seen[k] = true
			return true
		})

		if len(seen) != mapSize {
			t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize)
		}
	}
}


================================================
FILE: direct_api.go
================================================
package ecs

import (
	"reflect"
	"unsafe"
)

func RegisterSystem[T SystemObject, TP SystemPointer[T]](world IWorld, order ...Order) {
	sys := TP(new(T))
	if len(order) > 0 {
		sys.setOrder(order[0])
	}
	world.registerSystem(sys)
}

func AddFreeComponent[T FreeComponentObject, TP FreeComponentPointer[T]](world IWorld, component *T) {
	world.addFreeComponent(TP(component))
}

func GetComponent[T ComponentObject](sys ISystem, entity Entity) *T {
	return GetRelated[T](sys, entity)
}

func GetComponentAll[T ComponentObject](sys ISystem) Iterator[T] {
	if sys.getState() == SystemStateInvalid {
		return EmptyIter[T]()
	}
	if !sys.isExecuting() {
		return EmptyIter[T]()
	}
	typ := GetType[T]()
	r, ok := sys.GetRequirements()[typ]
	if !ok {
		return EmptyIter[T]()
	}

	c := sys.World().getComponentSet(typ)
	if c == nil {
		return EmptyIter[T]()
	}
	return NewComponentSetIterator[T](c.(*ComponentSet[T]), r.getPermission() == ComponentReadOnly)
}

func GetRelated[T ComponentObject](sys ISystem, entity Entity) *T {
	typ := TypeOf[T]()
	isRequire := sys.isRequire(typ)
	if !isRequire {
		return nil
	}
	var cache *ComponentGetter[T]
	cacheMap := sys.getGetterCache()
	c := cacheMap.Get(typ)
	if c != nil {
		cache = (*ComponentGetter[T])(c)
	} else {
		cache = NewComponentGetter[T](sys)
		cacheMap.Add(typ, unsafe.Pointer(cache))
	}
	return cache.Get(entity)
}

func BindUtility[T UtilityObject, TP UtilityPointer[T]](si SystemInitConstraint) {
	if si.isValid() {
		panic("out of initialization stage")
	}
	utility := TP(new(T))
	sys := si.getSystem()
	utility.setSystem(sys)
	utility.setWorld(sys.World())
	sys.setUtility(utility)
	sys.World().base().utilities[utility.Type()] = utility
}

func GetUtility[T UtilityObject, TP UtilityPointer[T]](getter IUtilityGetter) (*T, bool) {
	w := getter.getWorld()
	if w == nil {
		return nil, false
	}
	u, ok := w.getUtilityForT(TypeOf[T]())
	if !ok {
		return nil, false
	}
	return (*T)(u), true
}

func TypeOf[T any]() reflect.Type {
	ins := (*T)(nil)
	return reflect.TypeOf(ins).Elem()
}


================================================
FILE: entity.go
================================================
package ecs

import (
	"sort"
	"unsafe"
)

type Entity int64

func (e Entity) ToInt64() int64 {
	return int64(e)
}

func (e Entity) ToRealID() RealID {
	return *(*RealID)(unsafe.Pointer(&e))
}

type RealID struct {
	index int32
	reuse int32
}

func (r *RealID) ToInt64() int64 {
	return *(*int64)(unsafe.Pointer(r))
}

func (r *RealID) ToEntity() Entity {
	return *(*Entity)(unsafe.Pointer(r))
}

type EntityIDGenerator struct {
	ids     []RealID
	free    int32
	pending int32
	len     int32

	removeDelay []RealID
	delayFree   int32
	delayCap    int32
}

func NewEntityIDGenerator(initSize int, delayCap int) *EntityIDGenerator {
	g := &EntityIDGenerator{}
	g.ids = make([]RealID, initSize)
	for i := 0; i < len(g.ids); i++ {
		g.ids[i].index = int32(i + 1)
	}
	g.free = 1
	g.pending = int32(initSize)
	g.len = 0
	g.removeDelay = make([]RealID, delayCap)
	g.delayCap = int32(delayCap)
	g.delayFree = 0
	return g
}

func (e *EntityIDGenerator) NewID() Entity {
	id := RealID{}
	if e.free == e.pending {
		e.ids = append(e.ids, RealID{index: e.free, reuse: 0})
		id = e.ids[e.pending]
		e.free++
		e.pending++
	} else {
		next := e.ids[e.free].index
		e.ids[e.free].index = e.free
		id = e.ids[e.free]
		e.free = next
	}
	e.len++
	return id.ToEntity()
}

func (e *EntityIDGenerator) FreeID(entity Entity) {
	e.len--

	realID := entity.ToRealID()
	e.ids[realID.index].index = -1
	e.ids[realID.index].reuse++

	e.removeDelay[e.delayFree] = realID
	e.delayFree++
	if e.delayFree >= e.delayCap {
		e.delayFlush()
	}
	if e.pending > 1024 && e.pending < int32(len(e.ids))/2 {
		e.ids = e.ids[:e.len*5/8]
	}
}

func (e *EntityIDGenerator) delayFlush() {
	sort.Slice(e.removeDelay, func(i, j int) bool {
		return e.removeDelay[i].index < e.removeDelay[j].index
	})
	lastFree := e.free
	nextFree := e.free
	if e.free < e.pending {
		nextFree = e.ids[e.free].index
	}
	for i := int32(0); i < e.delayFree; i++ {
		tempID := e.removeDelay[i]
		if tempID.index < lastFree {
			e.ids[tempID.index].index = lastFree
			e.free = tempID.index
			nextFree = lastFree
			lastFree = tempID.index
			continue
		} else {
			for {
				if tempID.index < nextFree {
					e.ids[lastFree].index = tempID.index
					e.ids[tempID.index].index = nextFree
					lastFree = tempID.index
					break
				} else {
					lastFree = nextFree
					nextFree = e.ids[nextFree].index
				}
			}
		}
	}
	e.delayFree = 0
}


================================================
FILE: entity_info.go
================================================
package ecs

type EntityInfo struct {
	entity   Entity
	compound Compound
}

func (e *EntityInfo) Destroy(world IWorld) {
	for i := 0; i < len(e.compound); i++ {
		world.deleteComponentByIntType(e.entity, e.compound[i])
	}
	// must be last
	world.deleteEntity(e.entity)
}

func (e *EntityInfo) Entity() Entity {
	return e.entity
}

func (e *EntityInfo) Add(world IWorld, components ...IComponent) {
	for _, c := range components {
		if !e.compound.Exist(world.getComponentMetaInfoByType(c.Type()).it) {
			world.addComponent(e.entity, c)
		}
	}
}

func (e *EntityInfo) Has(its ...uint16) bool {
	for i := 0; i < len(its); i++ {
		if !e.compound.Exist(its[i]) {
			return false
		}
	}
	return true
}

func (e *EntityInfo) HasType(world *ecsWorld, components ...IComponent) bool {
	for i := 0; i < len(components); i++ {
		if !e.compound.Exist(world.getComponentMetaInfoByType(components[i].Type()).it) {
			return false
		}
	}
	return true
}

func (e *EntityInfo) addToCompound(it uint16) {
	e.compound.Add(it)
}

func (e *EntityInfo) removeFromCompound(it uint16) {
	e.compound.Remove(it)
}

func (e *EntityInfo) Remove(world IWorld, components ...IComponent) {
	for _, c := range components {
		if e.compound.Exist(world.getComponentMetaInfoByType(c.Type()).it) {
			world.deleteComponent(e.entity, c)
		}
	}
}


================================================
FILE: entity_set.go
================================================
package ecs

type EntitySet struct {
	SparseArray[int32, EntityInfo]
}

func NewEntityCollection() *EntitySet {
	return &EntitySet{
		SparseArray: *NewSparseArray[int32, EntityInfo](),
	}
}

func (c *EntitySet) Exist(entity Entity) bool {
	index := entity.ToRealID().index
	return c.SparseArray.Exist(index)
}

func (c *EntitySet) GetEntityInfo(entity Entity) (*EntityInfo, bool) {
	index := entity.ToRealID().index
	info := c.Get(index)
	if info == nil {
		return nil, false
	}
	return info, true
}

func (c *EntitySet) Add(entityInfo EntityInfo) *EntityInfo {
	index := entityInfo.entity.ToRealID().index
	return c.SparseArray.Add(index, &entityInfo)
}

func (c *EntitySet) Remove(entity Entity) *EntityInfo {
	index := entity.ToRealID().index
	return c.SparseArray.Remove(index)
}


================================================
FILE: entity_test.go
================================================
package ecs

import (
	"math/rand"
	"testing"
	"time"
	"unsafe"
)

func TestEntityIDGenerator_NewID(t *testing.T) {
	t.Run("test1", func(t *testing.T) {
		e := NewEntityIDGenerator(10, 3)
		id1 := e.NewID()
		id2 := e.NewID()
		id3 := e.NewID()

		e.FreeID(id2)

		id4 := e.NewID()

		e.FreeID(id1)
		e.FreeID(id4)
		e.FreeID(id3)

		var m []Entity
		for i := 0; i < 11; i++ {
			newID := e.NewID()
			m = append(m, newID)
		}

		for _, id := range m {
			e.FreeID(id)
		}
	})
}

type _EntityTest struct {
	seq      int32
	freeList map[RealID]struct{}
}

func (e *_EntityTest) NewID() Entity {
	id := RealID{}
	if len(e.freeList) > 0 {
		for i, _ := range e.freeList {
			id = i
			delete(e.freeList, i)
		}
	} else {
		id = RealID{index: e.seq, reuse: 0}
		e.seq++
	}
	return id.ToEntity()
}

func (e *_EntityTest) FreeID(id Entity) {
	real := *(*RealID)(unsafe.Pointer(&id))
	e.freeList[real] = struct{}{}
}

func BenchmarkEntityIDGenerator_New(b *testing.B) {
	e := NewEntityIDGenerator(1024, 10)
	idmap := map[Entity]struct{}{}
	e2 := &_EntityTest{freeList: map[RealID]struct{}{}}
	idmap2 := map[Entity]struct{}{}

	for i := 0; i < 10000; i++ {
		id := e.NewID()
		idmap[id] = struct{}{}

		id = e2.NewID()
		idmap2[id] = struct{}{}
	}

	b.Run("map", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			e.NewID()
		}
	})
	b.Run("gen", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			e2.NewID()
		}
	})
}

func TestEntityIDGenerator_Free(t *testing.T) {
	size := 100000
	e := NewEntityIDGenerator(1024, 100)
	idmap := map[Entity]struct{}{}
	for i := 0; i < size; i++ {
		id := e.NewID()
		idmap[id] = struct{}{}
	}

	e2 := &_EntityTest{freeList: map[RealID]struct{}{}}
	idmap2 := map[Entity]struct{}{}
	for i := 0; i < size; i++ {
		id := e2.NewID()
		idmap2[id] = struct{}{}
	}

	t.Run("gen", func(t *testing.T) {
		start := time.Now()
		for i, _ := range idmap {
			e.FreeID(i)
		}
		println(time.Since(start).Nanoseconds() / int64(size))
	})
	t.Run("map", func(t *testing.T) {
		start := time.Now()
		for i, _ := range idmap2 {
			e2.FreeID(i)
		}
		println(time.Since(start).Nanoseconds() / int64(size))
	})
}

func BenchmarkEntityIDGenerator_NewFreeRandom(b *testing.B) {

	e := NewEntityIDGenerator(1024, 100)
	idmap := map[Entity]struct{}{}
	e2 := &_EntityTest{freeList: map[RealID]struct{}{}}
	idmap2 := map[Entity]struct{}{}

	for i := 0; i < 1000000; i++ {
		id := e.NewID()
		idmap[id] = struct{}{}

		id = e2.NewID()
		idmap2[id] = struct{}{}
	}

	b.Run("map", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			r := rand.Intn(100)
			if r%2 == 0 {
				id := e.NewID()
				idmap[id] = struct{}{}
			} else {
				for i2, _ := range idmap {
					e.FreeID(i2)
					delete(idmap, i2)
					break
				}
			}
		}
	})
	b.Run("gen", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			r := rand.Intn(100)
			if r%2 == 0 {
				id := e2.NewID()
				idmap[id] = struct{}{}
			} else {
				for i2, _ := range idmap {
					e2.FreeID(i2)
					delete(idmap2, i2)
					break
				}
			}
		}
	})
}


================================================
FILE: example/benchmark-0/components.go
================================================
package main

import (
	"github.com/zllangct/ecs"
)

type Position struct {
	ecs.Component[Position]
	X int
	Y int
	Z int
}

type Movement struct {
	ecs.Component[Movement]
	V   int
	Dir [3]int
}

type HealthPoint struct {
	ecs.Component[HealthPoint]
	HP int
}

type Force struct {
	ecs.Component[Force]
	AttackRange        int
	PhysicalBaseAttack int
	Strength           int
	CriticalChange     int
	CriticalMultiple   int
}

type Action struct {
	ecs.DisposableComponent[Action]
	ActionType int
}

type Test1 struct {
	ecs.Component[Test1]
	Test1 int
}

type Test2 struct {
	ecs.Component[Test2]
	Test2 int
}

type Test3 struct {
	ecs.Component[Test3]
	Test3 int
}

type Test4 struct {
	ecs.Component[Test4]
	Test4 int
}

type Test5 struct {
	ecs.Component[Test5]
	Test5 int
}

type Test6 struct {
	ecs.Component[Test6]
	Test6 int
}

type Test7 struct {
	ecs.Component[Test7]
	Test7 int
}

type Test8 struct {
	ecs.Component[Test8]
	Test8 int
}

type Test9 struct {
	ecs.Component[Test9]
	Test9 int
}

type Test10 struct {
	ecs.Component[Test10]
	Test10 int
}


================================================
FILE: example/benchmark-0/damage_system.go
================================================
package main

import (
	"errors"
	"github.com/zllangct/ecs"
	"math/rand"
)

type Caster struct {
	Action   *Action
	Position *Position
	Force    *Force
}

type Target struct {
	HealthPoint *HealthPoint
	Position    *Position
}

type DamageSystem struct {
	ecs.System[DamageSystem]
	casterGetter *ecs.Shape[Caster]
	targetGetter *ecs.Shape[Target]
}

func (d *DamageSystem) Init(si ecs.SystemInitConstraint) error {
	d.SetRequirements(
		si,
		//&ecs.ReadOnly[Position]{},
		//&ecs.ReadOnly[Force]{},
		//&ecs.ReadOnly[Action]{},
		&Position{},
		&Force{},
		&Action{},
		&HealthPoint{})
	d.casterGetter = ecs.NewShape[Caster](si).SetGuide(&Action{})
	d.targetGetter = ecs.NewShape[Target](si)

	if !d.casterGetter.IsValid() || !d.targetGetter.IsValid() {
		return errors.New("invalid shape getter")
	}
	return nil
}

// Update will be called every frame
func (d *DamageSystem) Update(event ecs.Event) {
	/*  伤害结算逻辑
	  - 分析'攻击'行为,我们需要考虑攻击的相关计算公式所需组件,如当前示例中的Force组件,包含基础攻击、力量、
	暴击倍率、暴击率、攻击范围等数据。考虑攻击范围时需要,知道位置相关信息,由Position组件提供数据支持,在全遍历
	所有位置关系时,消耗比较大,可通过AOI优化,减小遍历规模,优化搜索效率,此处示例不做额外处理。
	*/
	casterIter := d.casterGetter.Get()
	targetIter := d.targetGetter.Get()
	count := 0
	for caster := casterIter.Begin(); !casterIter.End(); caster = casterIter.Next() {
		//count++
		for target := targetIter.Begin(); !targetIter.End(); target = targetIter.Next() {
			if caster.Action.Owner() == target.HealthPoint.Owner() {
				continue
			}

			//计算距离
			distance := (caster.Position.X-target.Position.X)*(caster.Position.X-target.Position.X) +
				(caster.Position.Y-target.Position.Y)*(caster.Position.Y-target.Position.Y)
			if distance > caster.Force.AttackRange*caster.Force.AttackRange {
				continue
			}

			//伤害公式:伤害=(基础攻击+力量)+ 暴击伤害, 暴击伤害=基础攻击 * 2
			damage := caster.Force.PhysicalBaseAttack + caster.Force.Strength
			critical := 0
			if rand.Intn(100) < caster.Force.CriticalChange {
				critical = caster.Force.PhysicalBaseAttack * caster.Force.CriticalMultiple
			}
			damage = damage + critical
			//ecs.Log.Infof("Damage:%v", damage)
			target.HealthPoint.HP -= damage
			if target.HealthPoint.HP < 0 {
				target.HealthPoint.HP = 0
			}
			count++
		}
	}

	if count < (PlayerCount)*(PlayerCount-1) {
		//panic("count error")
	}

	//ecs.Log.Infof("DamageSystem count:%v, frame:%v", count, event.Frame)
}


================================================
FILE: example/benchmark-0/game_common.go
================================================
package main

const (
	PlayerCount = 200
	DummyMaxFor = 3000
)


================================================
FILE: example/benchmark-0/game_ecs.go
================================================
package main

import (
	"github.com/zllangct/ecs"
	_ "unsafe"
)

type GameECS struct {
	world    *ecs.SyncWorld
	entities []ecs.Entity
}

func (g *GameECS) init(config *ecs.WorldConfig) {
	g.world = ecs.NewSyncWorld(config)

	ecs.RegisterSystem[MoveSystem](g.world)
	ecs.RegisterSystem[DamageSystem](g.world)
	ecs.RegisterSystem[Test1System](g.world)
	ecs.RegisterSystem[Test2System](g.world)
	ecs.RegisterSystem[Test3System](g.world)
	ecs.RegisterSystem[Test4System](g.world)
	ecs.RegisterSystem[Test5System](g.world)
	ecs.RegisterSystem[Test6System](g.world)
	ecs.RegisterSystem[Test7System](g.world)
	ecs.RegisterSystem[Test8System](g.world)
	ecs.RegisterSystem[Test9System](g.world)
	ecs.RegisterSystem[Test10System](g.world)

	DataGenerateECS(g)
}

func (g *GameECS) attack() {
	act := &Action{
		ActionType: 1,
	}
	for _, entity := range g.entities {
		g.world.Add(entity, act)
	}
}

func DataGenerateECS(game *GameECS) {
	for i := 0; i < PlayerCount; i++ {
		p := &Position{
			X: 0,
			Y: 0,
			Z: 0,
		}
		m := &Movement{
			V:   100,
			Dir: [3]int{1, 0, 0},
		}
		h := &HealthPoint{
			HP: 100,
		}
		f := &Force{
			AttackRange:        10000,
			PhysicalBaseAttack: 10,
		}

		t1 := &Test1{}
		t2 := &Test2{}
		t3 := &Test3{}
		t4 := &Test4{}
		t5 := &Test5{}
		t6 := &Test6{}
		t7 := &Test7{}
		t8 := &Test8{}
		t9 := &Test9{}
		t10 := &Test10{}

		e := game.world.NewEntity()
		game.world.Add(e, p, m, h, f, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)

		game.entities = append(game.entities, e)
	}
}


================================================
FILE: example/benchmark-0/game_normal.go
================================================
package main

import (
	"math/rand"
	"sync"
	"time"
)

type Player struct {
	rw sync.RWMutex

	ID int64

	X                  int
	Y                  int
	Z                  int
	V                  int
	Dir                [3]int
	HP                 int
	AttackRange        int
	PhysicalBaseAttack int
	Strength           int
	CriticalChange     int
	CriticalMultiple   int
	ActionType         int
	Test1              int
	Test2              int
	Test3              int
	Test4              int
	Test5              int
	Test6              int
	Test7              int
	Test8              int
	Test9              int
	Test10             int
}

type GameNormal struct {
	players map[int64]*Player
}

func (g *GameNormal) init() {
	DataGenerateNormal(g)
}

func DataGenerateNormal(normal *GameNormal) {
	players := make(map[int64]*Player)
	for i := 0; i < PlayerCount; i++ {
		p := &Player{
			ID:                 int64(i),
			X:                  0,
			Y:                  0,
			Z:                  0,
			V:                  100,
			Dir:                [3]int{1, 0, 0},
			HP:                 100,
			AttackRange:        10000,
			PhysicalBaseAttack: 10,
			Strength:           0,
			CriticalChange:     0,
			CriticalMultiple:   0,
			ActionType:         1,
		}
		players[p.ID] = p
	}
	normal.players = players
}

func (g *GameNormal) doFrame(parallel bool, frame uint64, delta time.Duration) {
	if parallel {
		wg := &sync.WaitGroup{}
		wg.Add(12)
		go func() {
			g.DoMoveParallel(delta)
			wg.Done()
		}()
		go func() {
			g.DoDamageParallel()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel1()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel2()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel3()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel4()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel5()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel6()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel7()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel8()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel9()
			wg.Done()
		}()
		go func() {
			g.SimuLoadParallel10()
			wg.Done()
		}()
		wg.Wait()
	} else {
		// 移动
		g.DoMove(delta)
		// 攻击处理
		g.DoDamage()
		// 模拟其他负载
		g.SimuLoad1()
		g.SimuLoad2()
		g.SimuLoad3()
		g.SimuLoad4()
		g.SimuLoad5()
		g.SimuLoad6()
		g.SimuLoad7()
		g.SimuLoad8()
		g.SimuLoad9()
		g.SimuLoad10()
	}
}
func (g *GameNormal) SimuLoad1() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test1 += 1
		}
	}
}
func (g *GameNormal) SimuLoad2() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test2 += 1
		}
	}
}
func (g *GameNormal) SimuLoad3() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test3 += 1
		}
	}
}
func (g *GameNormal) SimuLoad4() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test4 += 1
		}
	}
}
func (g *GameNormal) SimuLoad5() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoad6() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoad7() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoad8() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoad9() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoad10() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test10 += 1
		}
	}
}

func (g *GameNormal) SimuLoadParallel1() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test1 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel2() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test2 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel3() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test3 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel4() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test4 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel5() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test5 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel6() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test6 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel7() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test7 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel8() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test8 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel9() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test9 += 1
		}
	}
}
func (g *GameNormal) SimuLoadParallel10() {
	for _, p := range g.players {
		for i := 0; i < DummyMaxFor; i++ {
			p.Test10 += 1
		}
	}
}

func (g *GameNormal) DoMove(delta time.Duration) {
	for _, p := range g.players {
		p.X = p.X + int(float64(p.Dir[0]*p.V)*delta.Seconds())
		p.Y = p.Y + int(float64(p.Dir[1]*p.V)*delta.Seconds())
		p.Z = p.Z + int(float64(p.Dir[2]*p.V)*delta.Seconds())
	}
}

func (g *GameNormal) DoMoveParallel(delta time.Duration) {
	for _, p := range g.players {
		p.X = p.X + int(float64(p.Dir[0]*p.V)*delta.Seconds())
		p.Y = p.Y + int(float64(p.Dir[1]*p.V)*delta.Seconds())
		p.Z = p.Z + int(float64(p.Dir[2]*p.V)*delta.Seconds())
	}
}

func (g *GameNormal) DoDamage() {
	for _, caster := range g.players {
		for _, target := range g.players {
			if caster.ID == target.ID {
				continue
			}

			//计算距离
			distance := (caster.X-target.X)*(caster.X-target.X) + (caster.Y-target.Y)*(caster.Y-target.Y)
			if distance > caster.AttackRange*caster.AttackRange {
				continue
			}

			//伤害公式:伤害=(基础攻击+力量)+ 暴击伤害, 暴击伤害=基础攻击 * 2
			damage := caster.PhysicalBaseAttack + caster.Strength
			critical := 0
			if rand.Intn(100) < caster.CriticalChange {
				critical = caster.PhysicalBaseAttack * caster.CriticalMultiple
			}
			damage = damage + critical
			target.HP -= damage
			if target.HP < 0 {
				target.HP = 0
			}
		}
	}
}
func (g *GameNormal) DoDamageParallel() {
	for _, caster := range g.players {
		caster.rw.RLock()
		for _, target := range g.players {
			if caster.ID == target.ID {
				continue
			}

			target.rw.Lock()
			//计算距离
			distance := (caster.X-target.X)*(caster.X-target.X) + (caster.Y-target.Y)*(caster.Y-target.Y)
			if distance > caster.AttackRange*caster.AttackRange {
				continue
			}

			//伤害公式:伤害=(基础攻击+力量)+ 暴击伤害, 暴击伤害=基础攻击 * 2
			damage := caster.PhysicalBaseAttack + caster.Strength
			critical := 0
			if rand.Intn(100) < caster.CriticalChange {
				critical = caster.PhysicalBaseAttack * caster.CriticalMultiple
			}
			damage = damage + critical
			target.HP -= damage
			if target.HP < 0 {
				target.HP = 0
			}
			target.rw.Unlock()
		}
		caster.rw.RUnlock()
	}
}


================================================
FILE: example/benchmark-0/go.mod
================================================
module test_ecs_m_d

go 1.18

replace github.com/zllangct/ecs => ./../..

require (
	github.com/zllangct/ecs v0.0.0
)

================================================
FILE: example/benchmark-0/main_benchmark_test.go
================================================
package main

import (
	"github.com/zllangct/ecs"
	"runtime"
	"testing"
	"time"
)

func BenchmarkNormal(b *testing.B) {
	game := &GameNormal{
		players: make(map[int64]*Player),
	}
	game.init()
	b.ResetTimer()

	var delta time.Duration
	var ts time.Time
	for i := 0; i < b.N; i++ {
		ts = time.Now()
		game.doFrame(false, uint64(i), delta)
		delta = time.Since(ts)
	}
}

func BenchmarkNormalParallel(b *testing.B) {
	game := &GameNormal{
		players: make(map[int64]*Player),
	}
	game.init()
	b.ResetTimer()

	var delta time.Duration
	var ts time.Time
	for i := 0; i < b.N; i++ {
		ts = time.Now()
		game.doFrame(true, uint64(i), delta)
		delta = time.Since(ts)
	}
}

func BenchmarkEcs(b *testing.B) {
	//go func() {
	//	http.ListenAndServe(":6060", nil)
	//}()

	game := &GameECS{}
	config := ecs.NewDefaultWorldConfig()
	config.Debug = false
	config.MetaInfoDebugPrint = false
	game.init(config)

	game.world.Startup()

	b.ResetTimer()

	var delta time.Duration
	_ = delta
	var ts time.Time
	for i := 0; i < b.N; i++ {
		ts = time.Now()
		game.attack()
		game.world.Update()
		delta = time.Since(ts)
	}
}

func BenchmarkEcsSingleCore(b *testing.B) {
	//go func() {
	//	http.ListenAndServe(":6060", nil)
	//}()

	runtime.GOMAXPROCS(1)

	game := &GameECS{}
	config := ecs.NewDefaultWorldConfig()
	config.Debug = false
	config.MetaInfoDebugPrint = false
	game.init(config)

	game.world.Startup()

	b.ResetTimer()

	var delta time.Duration
	_ = delta
	var ts time.Time
	for i := 0; i < b.N; i++ {
		ts = time.Now()
		game.attack()
		game.world.Update()
		delta = time.Since(ts)
	}
}


================================================
FILE: example/benchmark-0/main_test.go
================================================
package main

import (
	"fmt"
	"github.com/zllangct/ecs"
	_ "net/http/pprof"
	"reflect"
	"testing"
	"time"
	"unsafe"
	_ "unsafe"
)

func TestFrame(t *testing.T) {
	game := &GameECS{}
	game.init(ecs.NewDefaultWorldConfig())

	game.world.Startup()

	var delta time.Duration
	_ = delta
	var ts time.Time
	for i := 0; i < 10; i++ {
		ts = time.Now()
		game.world.Update()
		game.attack()
		delta = time.Since(ts)
		//ecs.Log.Info("===== Frame:", i, "=====", delta)
	}
}

func TestEcsOptimizer(t *testing.T) {
	game := &GameECS{}
	game.init(ecs.NewDefaultWorldConfig())

	game.world.Startup()

	var frameInterval = time.Millisecond * 33
	var delta time.Duration
	var ts time.Time
	for i := 0; i < 10; i++ {
		//ecs.Log.Info("===== Frame:", i)
		ts = time.Now()

		game.world.Update()
		game.attack()
		delta = time.Since(ts)
		ecs.Log.Info("===== Frame:", i, "=====", delta)
		if frameInterval-delta != 0 {
			game.world.Optimize(frameInterval-delta, true)
			time.Sleep(frameInterval - delta)
			delta = frameInterval
		}
	}
}

func TestOthers(t *testing.T) {
	var a1 = [...]reflect.Type{ecs.TypeOf[Test1](), ecs.TypeOf[Test2](), ecs.TypeOf[Test3]()}
	var a2 = [...]reflect.Type{ecs.TypeOf[Test1](), ecs.TypeOf[Test2](), ecs.TypeOf[Test3]()}
	m := map[interface{}]string{}
	m[a1] = "this is a1"
	m[a2] = "this is a2"
	fmt.Printf("%v\n", m)

	println(unsafe.Sizeof(ecs.Component[Test1]{}))
}


================================================
FILE: example/benchmark-0/move_system.go
================================================
package main

import (
	"github.com/zllangct/ecs"
)

type MoveSystem struct {
	ecs.System[MoveSystem]
}

func (m *MoveSystem) Init(si ecs.SystemInitConstraint) error {
	m.SetRequirements(si, &Position{}, &ecs.ReadOnly[Movement]{})
	return nil
}

func (m *MoveSystem) Update(event ecs.Event) {
	delta := event.Delta

	count := 0
	iter := ecs.GetComponentAll[Movement](m)
	for move := iter.Begin(); !iter.End(); move = iter.Next() {
		entity := move.Owner()
		pos := ecs.GetRelated[Position](m, entity)
		if pos == nil {
			continue
		}

		pos.X = pos.X + int(float64(move.Dir[0]*move.V)*delta.Seconds())
		pos.Y = pos.Y + int(float64(move.Dir[1]*move.V)*delta.Seconds())
		pos.Z = pos.Z + int(float64(move.Dir[2]*move.V)*delta.Seconds())

		oldMoveV := move.V
		_ = oldMoveV

		// test for read only component, changes are not effective
		move.V = move.V + 1

		count++
		//ecs.Log.Info("target id:", entity, " delta:", delta, " current position:", pos.X, pos.Y, pos.Z,
		//	" move speed:", oldMoveV)
	}
}


================================================
FILE: example/benchmark-0/simu_load_system.go
================================================
package main

import "github.com/zllangct/ecs"

type Test1System struct {
	ecs.System[Test1System]
}

func (t *Test1System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test1{})
	return nil
}

func (t *Test1System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test1](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test1 += i
		}
	}
}

type Test2System struct {
	ecs.System[Test2System]
}

func (t *Test2System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test2{})
	return nil
}

func (t *Test2System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test2](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test2 += i
		}
	}
}

type Test3System struct {
	ecs.System[Test3System]
}

func (t *Test3System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test3{})
	return nil
}

func (t *Test3System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test3](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test3 += i
		}
	}
}

type Test4System struct {
	ecs.System[Test4System]
}

func (t *Test4System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test4{})
	return nil
}

func (t *Test4System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test4](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test4 += i
		}
	}
}

type Test5System struct {
	ecs.System[Test5System]
}

func (t *Test5System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test5{})
	return nil
}

func (t *Test5System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test5](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test5 += i
		}
	}
}

type Test6System struct {
	ecs.System[Test6System]
}

func (t *Test6System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test6{})
	return nil
}

func (t *Test6System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test6](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test6 += i
		}
	}
}

type Test7System struct {
	ecs.System[Test7System]
}

func (t *Test7System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test7{})
	return nil
}

func (t *Test7System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test7](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test7 += i
		}
	}
}

type Test8System struct {
	ecs.System[Test8System]
}

func (t *Test8System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test8{})
	return nil
}

func (t *Test8System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test8](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test8 += i
		}
	}
}

type Test9System struct {
	ecs.System[Test9System]
}

func (t *Test9System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test9{})
	return nil
}

func (t *Test9System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test9](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test9 += i
		}
	}
}

type Test10System struct {
	ecs.System[Test10System]
}

func (t *Test10System) Init(si ecs.SystemInitConstraint) error {
	t.SetRequirements(si, &Test10{})
	return nil
}

func (t *Test10System) Update(event ecs.Event) {
	iter := ecs.GetComponentAll[Test10](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		for i := 0; i < DummyMaxFor; i++ {
			c.Test10 += i
		}
	}
}


================================================
FILE: example/fake-simple-game-server/client/fake_client.go
================================================
package client

import (
	"context"
	"fmt"
	"github.com/zllangct/ecs"
	"math/rand"
	"test_ecs_fake_server/network"
	"time"
)

type FakeClient struct {
}

func NewClient() *FakeClient {
	return &FakeClient{}
}

func (f *FakeClient) Run(ctx context.Context) {
	var c []*network.TcpConn
	for i := 0; i < 5; i++ {
		conn := network.Dial("127.0.0.1:3333")
		c = append(c, conn)
	}

	// simulation to send pkg
	go func() {
		for {
			time.Sleep(time.Second * time.Duration(rand.Intn(5)))
			idx := rand.Intn(len(c))
			c[idx].Write(fmt.Sprintf("chat:hi, i am %d", idx))
		}
	}()

	// simulation to control player
	go func() {
		for {
			time.Sleep(time.Second * time.Duration(rand.Intn(5)))
			idx := rand.Intn(len(c))
			v := rand.Intn(1000)
			dir := [3]int{0, 0, 0}
			dir[rand.Intn(3)] = 1
			c[idx].Write(fmt.Sprintf("move:%d,%d,%d:%d", dir[0], dir[1], dir[2], v))
		}
	}()

	// simulation to accept pkg
	for i, conn := range c {
		go func(idx int, conn *network.TcpConn) {
			for {
				pkg := conn.Read()
				ecs.Log.Infof("client[%d] recv: %+v", idx, pkg)
			}
		}(i, conn)
	}

	<-ctx.Done()
}


================================================
FILE: example/fake-simple-game-server/game/chat.go
================================================
package game

import (
	"sync"
)

type ChatRoom struct {
	clients *sync.Map
}

func NewChatRoom(c *sync.Map) *ChatRoom {
	return &ChatRoom{
		clients: &sync.Map{},
	}
}

func (c *ChatRoom) Talk(content string) {
	c.clients.Range(func(k, v interface{}) bool {
		sess := v.(*Session)
		sess.Conn.Write(content)
		return true
	})
}


================================================
FILE: example/fake-simple-game-server/game/empty_system.go
================================================
package game

import "github.com/zllangct/ecs"

type EmptySystem struct {
	ecs.System[EmptySystem]
	isPreStart  bool
	isStart     bool
	isPostStart bool
}

func (e *EmptySystem) Init(si ecs.SystemInitConstraint) error {
	ecs.Log.Info("empty system init")
	return nil
}

func (e *EmptySystem) Start(event ecs.Event) {
	ecs.Log.Info("empty system start")
}

func (e *EmptySystem) PreUpdate(event ecs.Event) {
	if e.isPreStart {
		return
	}
	e.isPreStart = true
	ecs.Log.Info("empty system pre update")
}

func (e *EmptySystem) Update(event ecs.Event) {
	if e.isStart {
		return
	}
	e.isStart = true
	ecs.Log.Info("empty system update")
}

func (e *EmptySystem) PostUpdate(event ecs.Event) {
	if e.isPostStart {
		return
	}
	e.isPostStart = true
	ecs.Log.Info("empty system post update")
}


================================================
FILE: example/fake-simple-game-server/game/fake_game.go
================================================
package game

import (
	"context"
	"errors"
	"github.com/zllangct/ecs"
	"strconv"
	"strings"
	"sync"
	"test_ecs_fake_server/network"
)

var send chan Msg2Client = make(chan Msg2Client, 10)

type Msg2Client struct {
	SessionID int
	Content   interface{}
}

type FakeGame struct {
	clients  sync.Map
	world    *ecs.AsyncWorld
	chatRoom *ChatRoom
}

func NewGame() *FakeGame {
	return &FakeGame{}
}

func (f *FakeGame) Run(ctx context.Context) {
	f.InitEcs()
	f.InitChat()
	f.InitNetwork()
}

func (f *FakeGame) InitEcs() {
	//create config
	config := ecs.NewDefaultWorldConfig()

	//create a world and startup
	f.world = ecs.NewAsyncWorld(config)
	f.world.Startup()

	//register your system
	ecs.RegisterSystem[MoveSystem](f.world)
	ecs.RegisterSystem[SyncSystem](f.world)
	ecs.RegisterSystem[EmptySystem](f.world)
}

func (f *FakeGame) EnterGame(sess *Session) {
	f.world.Wait(func(gaw ecs.SyncWrapper) error {
		e := gaw.NewEntity()
		gaw.Add(e, &PlayerComponent{
			SessionID: sess.SessionID,
		})
		gaw.Add(e, &Position{
			X: 100,
			Y: 100,
			Z: 100,
		})
		gaw.Add(e, &Movement{
			V:   2000,
			Dir: [3]int{1, 0, 0},
		})
		sess.Entity = e
		return nil
	})
}

func (f *FakeGame) InitNetwork() {
	lis, err := network.Listen()
	if err != nil {
		return
	}

	go func() {
		for {
			select {
			case m := <-send:
				obj, ok := f.clients.Load(m.SessionID)
				if ok {
					sess := obj.(*Session)
					sess.Conn.Write(m.Content)
				}
			}
		}
	}()

	seq := 0
	for {
		conn := lis.Accept()
		seq++
		sess := &Session{
			SessionID: seq,
			Conn:      conn,
		}

		go func(conn *network.TcpConn, sess *Session) {
			f.OnClientEnter(sess)
			for {
				pkg := conn.Read()
				f.Dispatch(pkg, sess)
			}
		}(conn, sess)
	}
}

func SendToClient(sessionId int, content interface{}) {
	send <- Msg2Client{
		SessionID: sessionId,
		Content:   content,
	}
}

func (f *FakeGame) OnClientEnter(sess *Session) {
	f.clients.Store(sess.SessionID, sess)
	f.EnterGame(sess)
}

func (f *FakeGame) Dispatch(pkg interface{}, sess *Session) {
	content, ok := pkg.(string)
	if !ok {
		return
	}

	split := strings.Split(content, ":")
	op := split[0]

	switch op {
	case "chat":
		// not handle by ecs
		f.chatRoom.Talk(split[1])
	case "move":
		// handle by ecs
		if len(split) != 3 {
			return
		}
		d := strings.Split(split[1], ",")
		if len(d) != 3 {
			return
		}
		var dir [3]int
		for i := 0; i < 3; i++ {
			value, _ := strconv.Atoi(d[i])
			dir[i] = value
		}

		v, _ := strconv.Atoi(split[2])
		f.Move(sess.Entity, v, dir)
	}
}

func (f *FakeGame) Move(entity ecs.Entity, v int, dir [3]int) {
	if f.world == nil {
		return
	}
	f.world.Sync(func(gaw ecs.SyncWrapper) error {
		u, ok := ecs.GetUtility[MoveSystemUtility](gaw)
		if !ok {
			return errors.New("can not find MoveSystemUtility")
		}
		return u.Move(entity, v, dir)
	})
}

func (f *FakeGame) ChangeMovementTimeScale(timeScale float64) {
	if f.world == nil {
		return
	}
	f.world.Sync(func(gaw ecs.SyncWrapper) error {
		u, ok := ecs.GetUtility[MoveSystemUtility](gaw)
		if !ok {
			return errors.New("can not find MoveSystemUtility")
		}
		return u.UpdateTimeScale(timeScale)
	})
}

func (f *FakeGame) InitChat() {
	f.chatRoom = NewChatRoom(&f.clients)
}


================================================
FILE: example/fake-simple-game-server/game/move_component.go
================================================
package game

import "github.com/zllangct/ecs"

type Position struct {
	ecs.Component[Position]
	X int
	Y int
	Z int
}

type Movement struct {
	ecs.Component[Movement]
	V   int
	Dir [3]int
}


================================================
FILE: example/fake-simple-game-server/game/move_system.go
================================================
package game

import (
	"errors"
	"github.com/zllangct/ecs"
	"time"
)

type MoveSystemUtility struct {
	ecs.Utility[MoveSystemUtility]
}

func (m *MoveSystemUtility) UpdateTimeScale(scale float64) error {
	s := m.GetSystem()
	if s == nil {
		return errors.New("system is nil")
	}
	sys := s.(*MoveSystem)
	sys.timeScale = scale
	return nil
}

func (m *MoveSystemUtility) Move(entity ecs.Entity, v int, dir [3]int) error {
	s := m.GetSystem()
	if s == nil {
		return errors.New("system is nil")
	}
	mov := ecs.GetComponent[Movement](s, entity)
	mov.V = v
	mov.Dir = dir
	return nil
}

type MoveSystemData struct {
	P *Position
	M *Movement
}

type MoveSystem struct {
	ecs.System[MoveSystem]
	timeScale float64
	deltaTime time.Duration
	getter    *ecs.Shape[MoveSystemData]
}

func (m *MoveSystem) Init(si ecs.SystemInitConstraint) {
	m.SetRequirements(si, &Position{}, &Movement{})
	ecs.BindUtility[MoveSystemUtility](si)
	m.getter = ecs.NewShape[MoveSystemData](si)
}

func (m *MoveSystem) UpdateTimeScale(timeScale []interface{}) error {
	ecs.Log.Info("time scale change to ", timeScale[0])
	m.timeScale = timeScale[0].(float64)
	return nil
}

func (m *MoveSystem) Update(event ecs.Event) {
	delta := event.Delta

	m.deltaTime += delta
	isPrint := false
	if m.deltaTime > time.Second*3 {
		isPrint = true
		m.deltaTime = 0
	}

	iter := m.getter.Get()
	for shp := iter.Begin(); !iter.End(); shp = iter.Next() {
		mv := shp.M
		p := shp.P
		_, _ = p, mv
		p.X = p.X + int(float64(mv.Dir[0]*mv.V)*delta.Seconds())
		p.Y = p.Y + int(float64(mv.Dir[1]*mv.V)*delta.Seconds())
		p.Z = p.Z + int(float64(mv.Dir[2]*mv.V)*delta.Seconds())

		if isPrint {
			e := p.Owner()
			ecs.Log.Info("target id:", e, " delta:", delta, " current position:", p.X, p.Y, p.Z)
		}
	}
}


================================================
FILE: example/fake-simple-game-server/game/player_component.go
================================================
package game

import "github.com/zllangct/ecs"

type PlayerComponent struct {
	ecs.Component[PlayerComponent]
	Name      string
	Level     int
	SessionID int
}


================================================
FILE: example/fake-simple-game-server/game/position_sync_system.go
================================================
package game

import "github.com/zllangct/ecs"

type PlayerPosition struct {
	SessionID int
	Pos       Position
}

type SyncSystem struct {
	ecs.System[SyncSystem]
}

func (m *SyncSystem) Init(si ecs.SystemInitConstraint) error {
	m.SetRequirements(si, &Position{}, &PlayerComponent{})
	return nil
}

func (m *SyncSystem) PostUpdate(event ecs.Event) {
	p := ecs.GetComponentAll[Position](m)
	for i := p.Begin(); !p.End(); i = p.Next() {
		pc := ecs.GetRelated[PlayerComponent](m, i.Owner())
		if pc == nil {
			continue
		}
		SendToClient(pc.SessionID, PlayerPosition{
			SessionID: pc.SessionID,
			Pos:       *i,
		})
	}
}


================================================
FILE: example/fake-simple-game-server/game/session.go
================================================
package game

import (
	"github.com/zllangct/ecs"
	"test_ecs_fake_server/network"
)

type Session struct {
	SessionID int
	Conn      *network.TcpConn
	Entity    ecs.Entity
}


================================================
FILE: example/fake-simple-game-server/gm/gm.go
================================================
package gm

import (
	"context"
	"math/rand"
	"test_ecs_fake_server/game"
	"time"
)

type GM struct {
	game *game.FakeGame
}

func NewGM() *GM {
	return &GM{}
}

func (g *GM) Run(ctx context.Context, game *game.FakeGame) {
	g.game = game

	timeScale := 0
	for {
		time.Sleep(time.Second * time.Duration(rand.Intn(5)))
		if timeScale == 0 {
			g.ChangeMovementTimeScale(1.2)
			timeScale = 1
		} else {
			g.ChangeMovementTimeScale(1.0)
			timeScale = 0
		}
	}
}

func (g *GM) ChangeMovementTimeScale(timeScale float64) {
	g.game.ChangeMovementTimeScale(timeScale)
}


================================================
FILE: example/fake-simple-game-server/go.mod
================================================
module test_ecs_fake_server

go 1.18

replace github.com/zllangct/ecs => ./../..

require github.com/zllangct/ecs v0.0.0


================================================
FILE: example/fake-simple-game-server/main.go
================================================
package main

import (
	"context"
	"github.com/zllangct/ecs"
	"net/http"
	_ "net/http/pprof"
	"test_ecs_fake_server/client"
	"test_ecs_fake_server/game"
	"test_ecs_fake_server/gm"
)

func main() {
	ecs.Log.Info("game start...")
	go func() {
		ecs.Log.Info(http.ListenAndServe("localhost:8889", nil))
	}()

	ctx := context.Background()

	//my game
	game := game.NewGame()
	//game manager
	gm := gm.NewGM()
	////client manager
	cm := client.NewClient()

	go game.Run(ctx)
	go gm.Run(ctx, game)
	go cm.Run(ctx)

	<-ctx.Done()
	ecs.Log.Info("game end...")
}


================================================
FILE: example/fake-simple-game-server/network/fake_net.go
================================================
package network

type TcpConn struct {
	r chan interface{}
	w chan interface{}
}

func NewTcpConn() *TcpConn {
	return &TcpConn{
		r: make(chan interface{}, 10),
		w: make(chan interface{}, 10),
	}
}

func (t *TcpConn) Write(in interface{}) {
	//ecs.Log.Info("Tcp Send message:", in)
	t.w <- in
}

func (t *TcpConn) Read() interface{} {
	read := <-t.r
	//ecs.Log.Info("Tcp Read message:", read)
	return read
}

var ch chan *TcpConn = make(chan *TcpConn, 10)

type FakeTcpServer struct{}

func Listen() (*FakeTcpServer, error) {
	return &FakeTcpServer{}, nil
}

func Dial(addr string) *TcpConn {
	connSrc := NewTcpConn()
	connDst := NewTcpConn()
	connDst.r, connDst.w = connSrc.w, connSrc.r
	ch <- connDst
	return connSrc
}

func (f *FakeTcpServer) Accept() *TcpConn {
	return <-ch
}


================================================
FILE: fixed_string.go
================================================
package ecs

import (
	"fmt"
	"unsafe"
)

const (
	__FixedMax = 128
)

type FixedString[T any] struct {
	data T
	len  int
}

func (f *FixedString[T]) Clear() {
	f.len = 0
}

func (f *FixedString[T]) Empty() bool {
	return f.len == 0
}

func (f *FixedString[T]) Len() int {
	return f.len
}

func (f *FixedString[T]) String() string {
	return string((*(*[__FixedMax]byte)(unsafe.Pointer(&(f.data))))[:f.len])
}

func (f *FixedString[T]) Set(s string) {
	if len(s) > int(unsafe.Sizeof(f.data)) {
		panic(fmt.Sprintf("fixed string max size: %d, received size: %d", unsafe.Sizeof(f.data), len(s)))
	}
	f.len = len(s)
	if f.len != 0 {
		copy((*(*[__FixedMax]byte)(unsafe.Pointer(&(f.data))))[:unsafe.Sizeof(f.data)], s)
	}
}


================================================
FILE: fixed_string_test.go
================================================
package ecs

import (
	"bufio"
	"fmt"
	"os"
	"testing"
)

func TestFixedString_Generate(t *testing.T) {
	return
	filePath := "./fixed_string_utils.go"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("文件打开失败", err)
	}
	defer file.Close()
	write := bufio.NewWriter(file)
	h1 := `package ecs

`
	write.WriteString(h1)
	h2 := "type Fixed%d [%d]byte\n"
	for i := 1; i < 129; i++ {
		write.WriteString(fmt.Sprintf(h2, i, i))
	}
	write.Flush()
}

func TestFixedString_String(t *testing.T) {

	tests := []struct {
		name string
		arg  string
		want string
	}{
		{
			name: "test",
			arg:  "hello world",
			want: "hello world",
		},
		{
			name: "test1",
			arg:  "hello 中文 ☺",
			want: "hello 中文 ☺",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f := FixedString[Fixed128]{}
			f.Set(tt.arg)
			if got := f.String(); got != tt.want {
				t.Errorf("String() = %v, want %v", got, tt.want)
			}
		})
	}
}


================================================
FILE: fixed_string_utils.go
================================================
package ecs

type Fixed1 [1]byte
type Fixed2 [2]byte
type Fixed3 [3]byte
type Fixed4 [4]byte
type Fixed5 [5]byte
type Fixed6 [6]byte
type Fixed7 [7]byte
type Fixed8 [8]byte
type Fixed9 [9]byte
type Fixed10 [10]byte
type Fixed11 [11]byte
type Fixed12 [12]byte
type Fixed13 [13]byte
type Fixed14 [14]byte
type Fixed15 [15]byte
type Fixed16 [16]byte
type Fixed17 [17]byte
type Fixed18 [18]byte
type Fixed19 [19]byte
type Fixed20 [20]byte
type Fixed21 [21]byte
type Fixed22 [22]byte
type Fixed23 [23]byte
type Fixed24 [24]byte
type Fixed25 [25]byte
type Fixed26 [26]byte
type Fixed27 [27]byte
type Fixed28 [28]byte
type Fixed29 [29]byte
type Fixed30 [30]byte
type Fixed31 [31]byte
type Fixed32 [32]byte
type Fixed33 [33]byte
type Fixed34 [34]byte
type Fixed35 [35]byte
type Fixed36 [36]byte
type Fixed37 [37]byte
type Fixed38 [38]byte
type Fixed39 [39]byte
type Fixed40 [40]byte
type Fixed41 [41]byte
type Fixed42 [42]byte
type Fixed43 [43]byte
type Fixed44 [44]byte
type Fixed45 [45]byte
type Fixed46 [46]byte
type Fixed47 [47]byte
type Fixed48 [48]byte
type Fixed49 [49]byte
type Fixed50 [50]byte
type Fixed51 [51]byte
type Fixed52 [52]byte
type Fixed53 [53]byte
type Fixed54 [54]byte
type Fixed55 [55]byte
type Fixed56 [56]byte
type Fixed57 [57]byte
type Fixed58 [58]byte
type Fixed59 [59]byte
type Fixed60 [60]byte
type Fixed61 [61]byte
type Fixed62 [62]byte
type Fixed63 [63]byte
type Fixed64 [64]byte
type Fixed65 [65]byte
type Fixed66 [66]byte
type Fixed67 [67]byte
type Fixed68 [68]byte
type Fixed69 [69]byte
type Fixed70 [70]byte
type Fixed71 [71]byte
type Fixed72 [72]byte
type Fixed73 [73]byte
type Fixed74 [74]byte
type Fixed75 [75]byte
type Fixed76 [76]byte
type Fixed77 [77]byte
type Fixed78 [78]byte
type Fixed79 [79]byte
type Fixed80 [80]byte
type Fixed81 [81]byte
type Fixed82 [82]byte
type Fixed83 [83]byte
type Fixed84 [84]byte
type Fixed85 [85]byte
type Fixed86 [86]byte
type Fixed87 [87]byte
type Fixed88 [88]byte
type Fixed89 [89]byte
type Fixed90 [90]byte
type Fixed91 [91]byte
type Fixed92 [92]byte
type Fixed93 [93]byte
type Fixed94 [94]byte
type Fixed95 [95]byte
type Fixed96 [96]byte
type Fixed97 [97]byte
type Fixed98 [98]byte
type Fixed99 [99]byte
type Fixed100 [100]byte
type Fixed101 [101]byte
type Fixed102 [102]byte
type Fixed103 [103]byte
type Fixed104 [104]byte
type Fixed105 [105]byte
type Fixed106 [106]byte
type Fixed107 [107]byte
type Fixed108 [108]byte
type Fixed109 [109]byte
type Fixed110 [110]byte
type Fixed111 [111]byte
type Fixed112 [112]byte
type Fixed113 [113]byte
type Fixed114 [114]byte
type Fixed115 [115]byte
type Fixed116 [116]byte
type Fixed117 [117]byte
type Fixed118 [118]byte
type Fixed119 [119]byte
type Fixed120 [120]byte
type Fixed121 [121]byte
type Fixed122 [122]byte
type Fixed123 [123]byte
type Fixed124 [124]byte
type Fixed125 [125]byte
type Fixed126 [126]byte
type Fixed127 [127]byte
type Fixed128 [128]byte


================================================
FILE: go.mod
================================================
module github.com/zllangct/ecs

go 1.18


================================================
FILE: go.sum
================================================


================================================
FILE: goroutine_id.go
================================================
// Copyright ©2020 Dan Kortschak. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package goroutine provides a single function that will return the runtime's
// ID number for the calling goroutine.
//
// The implementation is derived from Laevus Dexter's comment in Gophers' Slack #darkarts,
// https://gophers.slack.com/archives/C1C1YSQBT/p1593885226448300 post which linked to
// this playground snippet https://play.golang.org/p/CSOp9wyzydP.

package ecs

import (
	"reflect"
	"unsafe"
)

// goroutineID returns the runtime ID of the calling goroutine.
func goroutineID() int64 {
	return *(*int64)(add(getg(), goidoff))
}

func getg() unsafe.Pointer {
	return *(*unsafe.Pointer)(add(getm(), curgoff))
}

//go:linkname add runtime.add
//go:nosplit
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer

//go:linkname getm runtime.getm
func getm() unsafe.Pointer

var (
	curgoff = offset("*runtime.m", "curg")
	goidoff = offset("*runtime.g", "goid")
)

// offset returns the offset into typ for the given field.
func offset(typ, field string) uintptr {
	rt := toType(typesByString(typ)[0])
	f, _ := rt.Elem().FieldByName(field)
	return f.Offset
}

//go:linkname typesByString reflect.typesByString
func typesByString(s string) []unsafe.Pointer

//go:linkname toType reflect.toType
func toType(t unsafe.Pointer) reflect.Type


================================================
FILE: goroutine_pool.go
================================================
package ecs

import (
	runtime2 "runtime"
)

// Worker goroutine struct.
type Worker struct {
	p        *Pool
	jobQueue chan func()
	stop     chan struct{}
}

// Start goroutine pool.
func (w *Worker) Start() {
	c := func() (c bool) {
		//defer func() {
		//	if r := recover(); r != nil {
		//		Log.Error(r)
		//	}
		//	c = true
		//}()
		var job func()
		for {
			select {
			case job = <-w.jobQueue:
			case job = <-w.p.jobQueue:
			case <-w.stop:
				return
			}
			job()
		}
	}()
	if c {
		go w.Start()
	}
}

// Pool is goroutine pool config.
type Pool struct {
	size         uint32
	jobQueueSize uint32
	jobQueue     chan func()
	workers      []*Worker
}

// NewPool news goroutine pool
func NewPool(size uint32, jobQueueSize uint32) *Pool {
	if size == 0 {
		size = uint32(2 * runtime2.NumCPU())
	}
	if jobQueueSize == 0 {
		jobQueueSize = uint32(runtime2.NumCPU())
	}
	jobQueue := make(chan func(), jobQueueSize*size)
	workerQueue := make([]*Worker, size)

	pool := &Pool{
		size:         uint32(size),
		jobQueueSize: uint32(jobQueueSize),
		jobQueue:     jobQueue,
		workers:      workerQueue,
	}
	for i := 0; i < cap(pool.workers); i++ {
		worker := &Worker{
			p:        pool,
			jobQueue: make(chan func(), pool.jobQueueSize),
			stop:     make(chan struct{}),
		}
		pool.workers[i] = worker
	}
	return pool
}

// Add hashKey is an optional parameter, job will be executed in a random worker
// when hashKey is regardless, in fixed worker calculated by hash when hashKey is
// specified
func (p *Pool) Add(job func(), hashKey ...uint32) {
	if len(hashKey) > 0 {
		p.workers[hashKey[0]%p.size].jobQueue <- job
		return
	}
	p.jobQueue <- job
}

// Start all workers
func (p *Pool) Start() {
	var worker *Worker
	for i := 0; i < cap(p.workers); i++ {
		worker = p.workers[i]
		go worker.Start()
	}
}

// Size get the pool size
func (p *Pool) Size() uint32 {
	return p.size
}

// Release rtStop all workers
func (p *Pool) Release() {
	for _, worker := range p.workers {
		worker.stop <- struct{}{}
	}
}


================================================
FILE: goroutine_pool_benchmark_test.go
================================================
package ecs

import (
	"sync"
	"testing"
)

const (
	runTimes  = 25
	poolSize  = 12
	queueSize = 50
)

func demoTask() {
	//time.Sleep(time.Nanosecond * 10)
}

// BenchmarkGoroutine benchmark the goroutine doing tasks.
func BenchmarkGoroutine(b *testing.B) {
	var wg sync.WaitGroup
	for i := 0; i < b.N; i++ {
		wg.Add(runTimes)

		for j := 0; j < runTimes; j++ {
			go func() {
				defer wg.Done()
				demoTask()
			}()
		}

		wg.Wait()
	}
}

// BenchmarkGpool benchmarks the goroutine pool.
func BenchmarkGpool(b *testing.B) {
	pool := NewPool(poolSize, queueSize)
	pool.Start()

	defer pool.Release()
	var wg sync.WaitGroup

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		wg.Add(runTimes)
		for j := 0; j < runTimes; j++ {
			pool.Add(func() {
				defer wg.Done()
				demoTask()
			})
		}

		wg.Wait()
	}
}


================================================
FILE: internal_type_mock.go
================================================
package ecs

import (
	"unsafe"
)

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
	Signed | Unsigned
}

// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
	~float32 | ~float64
}

// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
	~complex64 | ~complex128
}

// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
	Integer | Float | ~string
}

type eface struct {
	_type *_type
	data  unsafe.Pointer
}

type iface struct {
	tab  *itab
	data unsafe.Pointer
}

type itab struct {
	inter *interfacetype
	_type *_type
	hash  uint32
	_     [4]byte
	fun   [1]uintptr
}

type interfacetype struct {
	typ     _type
	pkgpath name
	mhdr    []imethod
}

type _type struct {
	size       uintptr
	ptrdata    uintptr
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	equal      func(unsafe.Pointer, unsafe.Pointer) bool
	gcdata     *byte
	str        nameOff
	ptrToThis  typeOff
}

type name struct {
	bytes *byte
}

type imethod struct {
	name nameOff // name of method
	typ  typeOff // .(*FuncType) underneath
}

type tflag uint8
type nameOff int32 // offset to a name
type typeOff int32 // offset to an *rtype


================================================
FILE: logger.go
================================================
package ecs

import (
	"fmt"
	"log"
	"os"
	"runtime"
)

type Logger interface {
	Debug(v ...interface{})
	Info(v ...interface{})
	Error(v ...interface{})
	Fatal(v ...interface{})

	Debugf(fmt string, v ...interface{})
	Infof(fmt string, v ...interface{})
	Errorf(fmt string, v ...interface{})
	Fatalf(fmt string, v ...interface{})
}

var Log Logger = NewStdLog()

type StdLogLevel uint8

const (
	StdLogLevelDebug StdLogLevel = iota
	StdLogLevelInfo
	StdLogLevelError
	StdLogLevelFatal
	StdLogLevelNoPrint
)

type StdLog struct {
	logger *log.Logger
	level  StdLogLevel
}

func NewStdLog(level ...StdLogLevel) *StdLog {
	l := StdLogLevelDebug
	if len(level) > 0 {
		l = level[0]
	}
	return &StdLog{
		level:  l,
		logger: log.New(os.Stdout, "", log.Lshortfile),
	}
}

func (p StdLog) Debug(v ...interface{}) {
	p.logger.Output(2, fmt.Sprintf("[DEBUG][%d] %s", goroutineID(), fmt.Sprint(v...)))
}

func (p StdLog) Debugf(format string, v ...interface{}) {
	p.logger.Output(2, fmt.Sprintf("[DEBUG][%d] %s", goroutineID(), fmt.Sprintf(format, v...)))
}

func (p StdLog) Info(v ...interface{}) {
	if p.level > StdLogLevelInfo {
		return
	}
	p.logger.Output(2, fmt.Sprint(v...))
}

func (p StdLog) Infof(format string, v ...interface{}) {
	if p.level > StdLogLevelInfo {
		return
	}
	p.logger.Output(2, fmt.Sprintf(format, v...))
}

func (p StdLog) Error(v ...interface{}) {
	if p.level > StdLogLevelError {
		return
	}
	buf := make([]byte, 1024)
	for {
		n := runtime.Stack(buf, false)
		if n < len(buf) {
			buf = buf[:n]
			break
		}
		buf = make([]byte, 2*len(buf))
	}
	s := fmt.Sprint(append(v, "\n", string(buf))...)
	p.logger.Output(2, s)
}

func (p StdLog) Errorf(format string, v ...interface{}) {
	if p.level > StdLogLevelError {
		return
	}
	buf := make([]byte, 1024)
	for {
		n := runtime.Stack(buf, false)
		if n < len(buf) {
			buf = buf[:n]
			break
		}
		buf = make([]byte, 2*len(buf))
	}
	s := fmt.Sprint(fmt.Sprintf(format, v...), "\n", string(buf))
	p.logger.Output(2, s)
}

func (p StdLog) Fatal(v ...interface{}) {
	if p.level > StdLogLevelFatal {
		return
	}
	p.Error(v...)
	os.Exit(1)
}

func (p StdLog) Fatalf(format string, v ...interface{}) {
	if p.level > StdLogLevelFatal {
		return
	}
	p.Errorf(format, v...)
	os.Exit(1)
}


================================================
FILE: metrics.go
================================================
package ecs

import "time"

type Metrics struct {
	enable  bool
	isPrint bool
	m       map[string]*MetricReporter
}

func (m *Metrics) NewReporter(name string) *MetricReporter {
	mr := &MetricReporter{
		name:          name,
		sampleElapsed: []reporterStep{},
	}
	mr.metrics = m
	return mr
}

func (m *Metrics) Print() {
	if !m.enable {
		return
	}
	for _, reporter := range m.m {
		reporter.Print()
	}
}

func NewMetrics(enable bool, print bool) *Metrics {
	return &Metrics{
		enable:  enable,
		isPrint: print,
		m:       make(map[string]*MetricReporter),
	}
}

type reporterStep struct {
	name    string
	elapsed time.Duration
}

type MetricReporter struct {
	name          string
	metrics       *Metrics
	start         time.Time
	last          time.Time
	sampleElapsed []reporterStep
	elapsedTotal  time.Duration
}

func (m *MetricReporter) Start() {
	if !m.metrics.enable {
		return
	}
	m.start = time.Now()
	m.last = m.start
}

func (m *MetricReporter) Sample(name string) {
	if !m.metrics.enable {
		return
	}
	now := time.Now()
	m.elapsedTotal = now.Sub(m.start)
	m.sampleElapsed = append(m.sampleElapsed, reporterStep{
		name:    name,
		elapsed: now.Sub(m.last),
	})
	m.last = now
}

func (m *MetricReporter) Stop() {
	if !m.metrics.enable {
		return
	}
	now := time.Now()
	m.elapsedTotal = now.Sub(m.start)
	if m.metrics != nil {
		m.metrics.m[m.name] = m
	}
}

func (m *MetricReporter) Print(force ...bool) {
	if !m.metrics.enable || !m.metrics.isPrint {
		if len(force) > 0 && force[0] {
		} else {
			return
		}
	}
	Log.Infof("%s: cost: %+v\n", m.name, m.elapsedTotal)
	for _, r := range m.sampleElapsed {
		Log.Infof("    ├─%20s: %+v\n", r.name, r.elapsed)
	}
}


================================================
FILE: optimizer.go
================================================
package ecs

import (
	"reflect"
	"sort"
	"time"
)

type ShapeInfo struct {
	typ    reflect.Type
	eNum   int64
	shapes []IShape
}

type OptimizerReporter struct {
	shapeUsage map[reflect.Type]IShape
}

func (o *OptimizerReporter) init() {
	o.shapeUsage = map[reflect.Type]IShape{}
}

type optimizer struct {
	world                  *ecsWorld
	startTime              time.Time
	expireTime             time.Time
	lastSample             time.Time
	shapeInfos             []*ShapeInfo
	lastCollectConsumption time.Duration
}

func newOptimizer(world *ecsWorld) *optimizer {
	return &optimizer{world: world}
}

// Collect 采集分布在各个系统中的OptimizerReporter
func (o *optimizer) collect() {
	start := time.Now()
	var opts []*OptimizerReporter
	for _, value := range o.world.systemFlow.systems {
		system, ok := value.(ISystem)
		if !ok {
			continue
		}
		if system != nil {
			opts = append(opts, system.getOptimizer())
		}
	}
	//all shapes
	var shapeRef = map[reflect.Type]*ShapeInfo{}
	for _, opt := range opts {
		for _, shp := range opt.shapeUsage {
			if info, ok := shapeRef[shp.getType()]; ok {
				info.eNum += shp.base().executeNum
			} else {
				shapeInfo := &ShapeInfo{
					typ:    shp.getType(),
					eNum:   shp.base().executeNum,
					shapes: []IShape{shp},
				}
				shapeRef[shp.getType()] = shapeInfo
			}
		}
	}
	//sort
	o.shapeInfos = []*ShapeInfo{}
	for _, info := range shapeRef {
		o.shapeInfos = append(o.shapeInfos, info)
	}
	sort.Slice(o.shapeInfos, func(i, j int) bool {
		return o.shapeInfos[i].eNum > o.shapeInfos[j].eNum
	})

	o.lastCollectConsumption = time.Since(start)
}

func (o *optimizer) optimize(IdleTime time.Duration, force bool) {
	Log.Infof("start optimize, rest time: %v", IdleTime)
	o.startTime = time.Now()
	o.lastSample = time.Now()
	o.expireTime = o.startTime.Add(IdleTime)

	o.collect()
	elapsed := o.elapsedStep()
	Log.Infof("collect step 1: %v", elapsed)

	o.memTidy(force)

	rest := o.expire()
	total := time.Now().Sub(o.startTime)
	Log.Infof("end optimize, rest time: %v, total: %v", rest, total)
}

func (o *optimizer) expire() time.Duration {
	return time.Until(o.expireTime)
}

func (o *optimizer) elapsed() time.Duration {
	return time.Now().Sub(o.startTime)
}

func (o *optimizer) elapsedStep() time.Duration {
	now := time.Now()
	r := now.Sub(o.lastSample)
	o.lastSample = now
	return r
}

func (o *optimizer) memTidy(force bool) {
	//seq := uint32(0)
	//m := map[interface{}][]*EntityInfo{}
	//o.world.entities.foreach(func(entity Entity, info *EntityInfo) bool {
	//	c := info.getCompound().Type()
	//	_, ok := m[c]
	//	if !ok {
	//		m[c] = []*EntityInfo{}
	//	}
	//	m[c] = append(m[c], info)
	//	return true
	//})
	//
	//elapsed := o.elapsedStep()
	//rest := o.expire()
	//Log.Infof("memTidy step 1: %v, expire: %v", elapsed, rest)
	//if !force && rest < time.Millisecond {
	//	return
	//}
	//
	//for _, infos := range m {
	//	for _, info := range infos {
	//		seq++
	//		for _, component := range info.components {
	//			component.setSeq(seq)
	//			c := o.world.components.getComponentSet(component.Type()).GetByEntity(int64(component.Owner().Entity()))
	//			verify := c.(IComponent)
	//			println(component.debugAddress(), verify.debugAddress())
	//			if verify.getSeq() != component.getSeq() {
	//				Log.Errorf("component seq error, %v, %v", verify.getSeq(), component.getSeq())
	//			}
	//		}
	//	}
	//}
	//
	//elapsed = o.elapsedStep()
	//rest = o.expire()
	//Log.Infof("memTidy step 2: %v, expire: %v", elapsed, rest)
	//if !force && rest < time.Millisecond {
	//	return
	//}
	//
	//for _, collection := range o.world.components.getCollections() {
	//	collection.Sort()
	//	if !force && o.expire() < time.Millisecond {
	//		break
	//	}
	//}
	//
	//elapsed = o.elapsedStep()
	//rest = o.expire()
	//Log.Infof("memTidy step 3: %v, expire: %v", elapsed, rest)
}


================================================
FILE: optimizer_benchmark_test.go
================================================
package ecs

import (
	"math/rand"
	"testing"
	"time"
)

const (
	testOptimizerDummyMaxFor = 10
	testOptimizerEntityMax   = 1000000
)

type __optimizer_Bench_C_1 struct {
	Component[__optimizer_Bench_C_1]
	Test1 int
}

type __optimizer_Bench_C_2 struct {
	Component[__optimizer_Bench_C_2]
	Test2 int
}

type __optimizer_Bench_S_1 struct {
	System[__optimizer_Bench_S_1]
}

func (t *__optimizer_Bench_S_1) Init(si SystemInitConstraint) {
	t.SetRequirements(si, &__optimizer_Bench_C_1{}, &__optimizer_Bench_C_2{})
}

func (t *__optimizer_Bench_S_1) Update(event Event) {
	iter := GetComponentAll[__optimizer_Bench_C_1](t)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		c2 := GetRelated[__optimizer_Bench_C_2](t, c.owner)
		if c2 == nil {
			continue
		}
		for i := 0; i < testOptimizerDummyMaxFor; i++ {
			c.Test1 += i
		}

		for i := 0; i < testOptimizerDummyMaxFor; i++ {
			c2.Test2 += i
		}
	}
}

type __optimizer_Bench_GameECS struct {
	world    *SyncWorld
	entities []Entity
}

func (g *__optimizer_Bench_GameECS) init() {
	println("init")
	config := NewDefaultWorldConfig()
	g.world = NewSyncWorld(config)

	RegisterSystem[__optimizer_Bench_S_1](g.world)

	for i := 0; i < testOptimizerEntityMax; i++ {
		c := &__optimizer_Bench_C_1{}
		e := g.world.newEntity()
		g.world.Add(e.Entity(), c)
		g.entities = append(g.entities, e.Entity())
	}
	rand.Seed(0)
	rand.Shuffle(len(g.entities), func(i, j int) { g.entities[i], g.entities[j] = g.entities[j], g.entities[i] })

	for i := 0; i < testOptimizerEntityMax; i++ {
		c := &__optimizer_Bench_C_2{}
		g.world.addComponent(g.entities[i], c)
	}
}

func BenchmarkNoOptimizer(b *testing.B) {
	//go func() {
	//	http.ListenAndServe(":6060", nil)
	//}()
	println("start")
	game := &__optimizer_Bench_GameECS{}
	game.init()
	game.world.update()

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		game.world.update()
	}
}

func BenchmarkWithOptimizer(b *testing.B) {
	//go func() {
	//	http.ListenAndServe(":6060", nil)
	//}()

	game := &__optimizer_Bench_GameECS{}
	game.init()
	game.world.update()

	game.world.optimize(time.Second*10, true)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		game.world.update()
	}
}

func BenchmarkTest(b *testing.B) {
	arr := make([]int, 0, 100)
	for i, _ := range arr {
		arr[i] = i
	}
	b.ResetTimer()

	for i := 0; i < b.N; i++ {

	}
}

func BenchmarkTest2(b *testing.B) {
	type test struct {
		Name string
		Age  int
	}
	t := test{Name: "test", Age: 1}
	m := map[test]int{t: 1}
	for i := 0; i < b.N; i++ {
		_ = m[t]
	}
}


================================================
FILE: ordered_int_set.go
================================================
package ecs

type OrderedIntSet[T Integer] []T

func (c *OrderedIntSet[T]) InsertIndex(it T) int {
	if len(*c) == 0 {
		return 0
	}
	l := 0
	r := len(*c) - 1
	m := 0
	for l < r {
		m = (l + r) / 2
		if (*c)[m] > it {
			r = m - 1
		} else if (*c)[m] < it {
			l = m + 1
		} else {
			return -1
		}
	}
	if (*c)[l] < it {
		l = l + 1
	} else if (*c)[l] > it {
	} else {
		l = l - 1
	}
	return l
}

func (c *OrderedIntSet[T]) Find(it T) int {
	l := 0
	r := len(*c) - 1
	m := 0
	for l <= r {
		m = (l + r) / 2
		if (*c)[m] == it {
			return m
		} else if (*c)[m] > it {
			r = m - 1
		} else {
			l = m + 1
		}
	}
	return -1
}

func (c *OrderedIntSet[T]) Exist(it T) bool {
	return c.Find(it) != -1
}

func (c *OrderedIntSet[T]) IsSubSet(subSet OrderedIntSet[T]) bool {
	offset := 0
	length := len(*c)
	exist := false
	var temp T
	for i := 0; i < len(subSet); i++ {
		exist = false
		temp = subSet[i]
		for j := offset; j < length; j++ {
			if (*c)[j] == temp {
				offset = j + 1
				exist = true
				break
			}
		}
		if !exist {
			return false
		}
	}
	return true
}

func (c *OrderedIntSet[T]) Add(it T) bool {
	idx := c.InsertIndex(it)
	if idx < 0 {
		return false
	}
	*c = append(*c, 0)
	copy((*c)[idx+1:], (*c)[idx:len(*c)-1])
	(*c)[idx] = it
	return true
}

func (c *OrderedIntSet[T]) Remove(it T) bool {
	idx := c.Find(it)
	if idx < 0 {
		return false
	}
	*c = append((*c)[:idx], (*c)[idx+1:]...)
	return true
}


================================================
FILE: ordered_int_set_test.go
================================================
package ecs

import "testing"

func TestOrderedIntSet_Add(t *testing.T) {
	c := OrderedIntSet[uint16]{}
	insert := []uint16{7, 3, 6, 2, 9, 4}
	for _, it := range insert {
		c.Add(it)
	}

	want := []uint16{2, 3, 4, 6, 7, 9}
	for i := 0; i < len(c); i++ {
		if c[i] != want[i] {
			t.Errorf("c[%d] = %d, want %d", i, c[i], want[i])
		}
	}
}

func TestOrderedIntSet_Remove(t *testing.T) {
	c := OrderedIntSet[uint16]{}
	insert := []uint16{7, 3, 6, 2, 9, 4}
	for _, it := range insert {
		c.Add(it)
	}

	c.Remove(3)

	c.Add(1)

	want := []uint16{1, 2, 4, 6, 7, 9}
	for i := 0; i < len(c); i++ {
		if c[i] != want[i] {
			t.Errorf("c[%d] = %d, want %d", i, c[i], want[i])
		}
	}
}

func TestOrderedIntSet_InsertIndex(t *testing.T) {
	c := OrderedIntSet[uint16]{}
	insert := []uint16{7, 3, 6, 2, 9, 4}
	for _, it := range insert {
		c.Add(it)
	}

	want := []uint16{2, 3, 4, 6, 7, 9}
	for i := 0; i < len(c); i++ {
		if c[i] != want[i] {
			t.Errorf("c[%d] = %d, want %d", i, c[i], want[i])
		}
	}

	wantIndex := 3
	if got := c.InsertIndex(5); got != wantIndex {
		t.Errorf("insertIndex() = %v, want %v", got, wantIndex)
	}
}

func TestOrderedIntSet_Find(t *testing.T) {
	c := OrderedIntSet[uint16]{}
	insert := []uint16{7, 3, 6, 2, 9, 4}
	for _, it := range insert {
		c.Add(it)
	}

	want := []uint16{2, 3, 4, 6, 7, 9}
	for i := 0; i < len(c); i++ {
		if c[i] != want[i] {
			t.Errorf("c[%d] = %d, want %d", i, c[i], want[i])
		}
	}

	wantIndex := 4
	if got := c.Find(7); got != wantIndex {
		t.Errorf("Find() = %v, want %v", got, wantIndex)
	}
}

func TestOrderedIntSet_IsSubSet(t *testing.T) {
	c := OrderedIntSet[uint16]{}
	insert := []uint16{7, 3, 6, 2, 9, 4}
	for _, it := range insert {
		c.Add(it)
	}

	want := []uint16{2, 3, 4, 6, 7, 9}
	for i := 0; i < len(c); i++ {
		if c[i] != want[i] {
			t.Errorf("c[%d] = %d, want %d", i, c[i], want[i])
		}
	}

	subSet := []uint16{3, 4, 6}
	wantBool := true
	if got := c.IsSubSet(subSet); got != wantBool {
		t.Errorf("IsSubSet() = %v, want %v", got, wantBool)
	}

	subSet = []uint16{2, 3, 4, 6, 7, 9}
	wantBool = true
	if got := c.IsSubSet(subSet); got != wantBool {
		t.Errorf("IsSubSet() = %v, want %v", got, wantBool)
	}

	subSet = []uint16{3, 4, 8}
	wantBool = false
	if got := c.IsSubSet(subSet); got != wantBool {
		t.Errorf("IsSubSet() = %v, want %v", got, wantBool)
	}
}


================================================
FILE: serialize.go
================================================
package ecs

// TODO 世界的序列化、反序列化
type ICustomSerialize interface {
	Serialize() []byte
	DeSerialize(b []byte)
}


================================================
FILE: shape_getter.go
================================================
package ecs

import (
	"reflect"
	"unsafe"
)

type IShape interface {
	base() *shapeBase
	getType() reflect.Type
}

type shapeBase struct {
	sys        ISystem
	executeNum int64
	typ        reflect.Type
}

func (s *shapeBase) base() *shapeBase {
	return s
}

func (s *shapeBase) init(typ reflect.Type, getter IShape) {
	opt := s.sys.getOptimizer()
	if _, ok := opt.shapeUsage[typ]; !ok {
		opt.shapeUsage[typ] = getter
	}
}

type ShapeIndices struct {
	subTypes   []uint16
	subOffset  []uintptr
	containers []IComponentSet
	readOnly   []bool
}

type Shape[T any] struct {
	shapeBase
	initializer  SystemInitConstraint
	mainKeyIndex int
	subTypes     []uint16
	subOffset    []uintptr
	containers   []IComponentSet
	readOnly     []bool
	cur          *T
	valid        bool
}

func NewShape[T any](initializer SystemInitConstraint) *Shape[T] {
	if initializer.isValid() {
		panic("out of initialization stage")
	}
	sys := initializer.getSystem()
	getter := &Shape[T]{
		shapeBase:   shapeBase{sys: sys},
		initializer: initializer,
	}

	typ := reflect.TypeOf(getter)
	getter.init(typ, getter)

	sysReq := sys.GetRequirements()
	if sysReq == nil {
		return nil
	}

	getter.cur = new(T)
	typIns := reflect.TypeOf(*getter.cur)
	for i := 0; i < typIns.NumField(); i++ {
		field := typIns.Field(i)
		if !field.Type.Implements(reflect.TypeOf((*IComponent)(nil)).Elem()) || !sys.isRequire(field.Type.Elem()) {
			continue
		}
		if r, ok := sysReq[field.Type.Elem()]; ok {
			if r.getPermission() == ComponentReadOnly {
				getter.readOnly = append(getter.readOnly, true)
			} else {
				getter.readOnly = append(getter.readOnly, false)
			}
		}
		meta := sys.World().getComponentMetaInfoByType(field.Type.Elem())
		getter.subTypes = append(getter.subTypes, meta.it)
		getter.subOffset = append(getter.subOffset, field.Offset)
	}

	getter.containers = make([]IComponentSet, len(getter.subTypes))

	if len(getter.subTypes) == 0 {
		return nil
	}

	getter.valid = true

	return getter
}

func (s *Shape[T]) IsValid() bool {
	return s.valid
}

func (s *Shape[T]) getType() reflect.Type {
	if s.typ == nil {
		s.typ = TypeOf[Shape[T]]()
	}
	return s.typ
}

func (s *Shape[T]) Get() IShapeIterator[T] {
	s.executeNum++

	if !s.valid {
		return EmptyShapeIter[T]()
	}

	var mainComponent IComponentSet
	var mainKeyIndex int
	for i := 0; i < len(s.subTypes); i++ {
		c := s.sys.World().getComponentSetByIntType(s.subTypes[i])
		if c == nil || c.Len() == 0 {
			return EmptyShapeIter[T]()
		}
		if mainComponent == nil || mainComponent.Len() > c.Len() {
			mainComponent = c
			mainKeyIndex = i
		}
		s.containers[i] = c
	}

	if s.mainKeyIndex == 0 {
		mainKeyIndex = s.mainKeyIndex
		mainComponent = s.containers[mainKeyIndex]
	}

	return NewShapeIterator[T](
		ShapeIndices{
			subTypes:   s.subTypes,
			subOffset:  s.subOffset,
			containers: s.containers,
			readOnly:   s.readOnly,
		},
		mainKeyIndex)
}

func (s *Shape[T]) GetSpecific(entity Entity) (*T, bool) {
	if !s.valid {
		return s.cur, false
	}
	for i := 0; i < len(s.subTypes); i++ {
		subPointer := s.containers[i].getPointerByEntity(entity)
		if subPointer == nil {
			return s.cur, false
		}
		if s.readOnly[i] {
			*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.subOffset[i])) = &(*(*byte)(subPointer))
		} else {
			*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.subOffset[i])) = (*byte)(subPointer)
		}
	}
	return s.cur, true
}

func (s *Shape[T]) SetGuide(component IComponent) *Shape[T] {
	meta := s.initializer.getSystem().World().getComponentMetaInfoByType(component.Type())
	for i, r := range s.subTypes {
		if r == meta.it {
			s.mainKeyIndex = i
			return s
		}
	}
	return s
}


================================================
FILE: shape_getter_test.go
================================================
package ecs

import (
	"testing"
	"time"
)

type __ShapeGetter_Test_C_1 struct {
	Component[__ShapeGetter_Test_C_1]
	Field1 int
}

type __ShapeGetter_Test_C_2 struct {
	Component[__ShapeGetter_Test_C_2]
	Field1 int
}

type __ShapeGetter_Test_Shape_1 struct {
	c1 *__ShapeGetter_Test_C_1
	c2 *__ShapeGetter_Test_C_2
}

type __ShapeGetter_Test_S_1 struct {
	System[__ShapeGetter_Test_S_1]

	getter1 *Shape[__ShapeGetter_Test_Shape_1]
}

func (t *__ShapeGetter_Test_S_1) Init(initializer SystemInitConstraint) {
	t.SetRequirements(initializer, &__ShapeGetter_Test_C_1{}, &__ShapeGetter_Test_C_2{})

	t.getter1 = NewShape[__ShapeGetter_Test_Shape_1](initializer)
	if t.getter1 == nil {
		initializer.SetBroken("invalid getter")
	}
}

func (t *__ShapeGetter_Test_S_1) Update(event Event) {
	Log.Infof("__ShapeGetter_Test_S_1.Update, frame:%d", event.Frame)
	iter := t.getter1.Get()
	for s := iter.Begin(); !iter.End(); s = iter.Next() {
		Log.Infof("s.c1:%+v, s.c2:%+v", s.c1, s.c2)
	}
}

func TestNewShapeGetter(t *testing.T) {
	world := NewSyncWorld(NewDefaultWorldConfig())
	RegisterSystem[__ShapeGetter_Test_S_1](world)

	world.Startup()

	for i := 0; i < 3; i++ {
		e := world.newEntity().Entity()
		world.Add(e, &__ShapeGetter_Test_C_1{Field1: i}, &__ShapeGetter_Test_C_2{Field1: i * 10})
	}

	world.Update()
	time.Sleep(time.Second)
	world.Update()
}


================================================
FILE: shape_iter.go
================================================
package ecs

import "unsafe"

type IShapeIterator[T any] interface {
	Begin() *T
	Val() *T
	Next() *T
	End() bool
}

type ShapeIter[T any] struct {
	indices      ShapeIndices
	maxLen       int
	offset       int
	begin        int
	mainKeyIndex int
	cur          *T
}

func EmptyShapeIter[T any]() IShapeIterator[T] {
	return &ShapeIter[T]{}
}

func NewShapeIterator[T any](indices ShapeIndices, mainKeyIndex int) IShapeIterator[T] {
	iter := &ShapeIter[T]{
		indices:      indices,
		maxLen:       indices.containers[mainKeyIndex].Len(),
		mainKeyIndex: mainKeyIndex,
		offset:       0,
	}

	return iter
}

// TODO 热点
func (s *ShapeIter[T]) tryNext() *T {
	skip := false
	find := false
	var p unsafe.Pointer
	var ec *EmptyComponent
	for i := s.offset; i < s.maxLen; i++ {
		//TODO check if this is the best way to do this
		p = s.indices.containers[s.mainKeyIndex].getPointerByIndex(int64(s.offset))
		ec = (*EmptyComponent)(p)
		if s.indices.readOnly[s.mainKeyIndex] {
			*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.indices.subOffset[s.mainKeyIndex])) = &(*(*byte)(p))
		} else {
			*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.indices.subOffset[s.mainKeyIndex])) = (*byte)(p)
		}
		entity := ec.Owner()
		skip = s.getSiblings(entity)
		if !skip {
			s.offset = i
			find = true
			break
		}
	}
	if !find {
		s.cur = nil
	}

	return s.cur
}

func (s *ShapeIter[T]) getSiblings(entity Entity) bool {
	for i := 0; i < len(s.indices.subTypes); i++ {
		if i == s.mainKeyIndex {
			continue
		}
		subPointer := s.indices.containers[i].getPointerByEntity(entity)
		if subPointer == nil {
			return true
		}
		s.trans(i, subPointer)
	}
	return false
}

func (s *ShapeIter[T]) trans(i int, subPointer unsafe.Pointer) {
	if s.indices.readOnly[i] {
		*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.indices.subOffset[i])) = &(*(*byte)(subPointer))
	} else {
		*(**byte)(unsafe.Add(unsafe.Pointer(s.cur), s.indices.subOffset[i])) = (*byte)(subPointer)
	}
}

func (s *ShapeIter[T]) End() bool {
	if s.cur == nil {
		return true
	}
	return false
}

func (s *ShapeIter[T]) Begin() *T {
	if s.maxLen != 0 {
		s.offset = 0
		s.cur = new(T)
		s.tryNext()
	}
	return s.cur
}

func (s *ShapeIter[T]) Val() *T {
	if s.cur == nil || !s.End() {
		s.Begin()
	}
	return s.cur
}

func (s *ShapeIter[T]) Next() *T {
	s.offset++
	if !s.End() {
		s.tryNext()
	} else {
		s.cur = nil
	}
	return s.cur
}


================================================
FILE: sparse_array.go
================================================
package ecs

type SparseArray[K Integer, V any] struct {
	UnorderedCollection[V]
	indices         []int32
	idx2Key         map[int32]int32
	maxKey          K
	shrinkThreshold int32
	initSize        int
}

func NewSparseArray[K Integer, V any](initSize ...int) *SparseArray[K, V] {
	typ := TypeOf[V]()
	eleSize := typ.Size()
	size := InitMaxSize / eleSize
	if len(initSize) > 0 {
		size = uintptr(initSize[0]) / eleSize
	}
	c := &SparseArray[K, V]{
		UnorderedCollection: UnorderedCollection[V]{
			data:    make([]V, 0, size),
			eleSize: eleSize,
		},
		idx2Key:  map[int32]int32{},
		initSize: int(size),
	}

	if size > 0 {
		c.indices = make([]int32, 0, size)
	}

	switch any(*new(K)).(type) {
	case int8, uint8:
		c.shrinkThreshold = 127
	case uint16:
		c.shrinkThreshold = 255
	default:
		c.shrinkThreshold = 1024
	}

	return c
}

func (g *SparseArray[K, V]) Add(key K, value *V) *V {
	length := len(g.indices)
	// already existed
	if key < K(length) && g.indices[key] != 0 {
		return nil
	}
	_, idx := g.UnorderedCollection.Add(value)
	if key >= K(length) {
		m := K(0)
		if length == 0 {
			m = key + 1
		} else if length < int(g.shrinkThreshold) {
			m = key * 2
		} else {
			m = key * 5 / 4
		}
		newIndices := make([]int32, m)
		count := copy(newIndices, g.indices)
		if count != length {
			panic("copy failed")
		}
		g.indices = newIndices
	}

	g.idx2Key[int32(idx)] = int32(key)
	g.indices[key] = int32(idx + 1)
	if key > g.maxKey {
		g.maxKey = key
	}

	return &g.data[idx]
}

func (g *SparseArray[K, V]) Remove(key K) *V {
	if key > g.maxKey {
		return nil
	}
	idx := g.indices[key] - 1
	removed, oldIndex, newIndex := g.UnorderedCollection.Remove(int64(idx))

	lastKey := g.idx2Key[int32(oldIndex)]
	g.indices[lastKey] = int32(newIndex + 1)
	g.indices[key] = 0
	g.idx2Key[idx] = lastKey
	delete(g.idx2Key, int32(oldIndex))

	g.shrink(key)

	return removed
}

func (g *SparseArray[K, V]) Exist(key K) bool {
	if key > g.maxKey {
		return false
	}
	return !(g.indices[key] == 0)
}

func (g *SparseArray[K, V]) Get(key K) *V {
	if key > g.maxKey {
		return nil
	}
	idx := g.indices[key] - 1
	if idx < 0 {
		return nil
	}
	return g.UnorderedCollection.Get(int64(idx))
}

func (g *SparseArray[K, V]) Clear() {
	if g.Len() == 0 {
		return
	}
	g.UnorderedCollection.Clear()
	if int(g.maxKey) < 1024 {
		for i := 0; i < len(g.indices); i++ {
			g.indices[i] = 0
		}
	} else {
		g.indices = make([]int32, 0, g.initSize)
	}
	g.maxKey = 0
	g.idx2Key = map[int32]int32{}
}

func (g *SparseArray[K, V]) shrink(key K) {
	if key < g.maxKey {
		return
	}

	g.maxKey = 0
	for i := key; i > 0; i-- {
		if g.indices[i] != 0 {
			g.maxKey = i
			break
		}
	}

	if int32(g.maxKey) < g.shrinkThreshold {
		g.maxKey = K(g.shrinkThreshold)
	}

	if len(g.indices) > 1024 && int(g.maxKey) < len(g.indices)/2 {
		m := (g.maxKey + 1) * 5 / 4
		newIndices := make([]int32, m)
		count := copy(newIndices, g.indices[:m])
		if count != int(m) {
			panic("copy failed")
		}
	}
}


================================================
FILE: system.go
================================================
package ecs

import (
	"reflect"
	"sync"
	"unsafe"
)

type SystemState uint8

const (
	SystemStateInvalid SystemState = iota
	SystemStateInit
	SystemStateStart
	SystemStatePause
	SystemStateUpdate
	SystemStateDestroy
	SystemStateDestroyed
)

type SystemInitConstraint struct {
	sys *ISystem
}

func (s *SystemInitConstraint) getSystem() ISystem {
	if s.sys == nil {
		panic("out of initialization stage")
	}
	return *s.sys
}

func (s *SystemInitConstraint) SetBroken(reason string) {
	(*s.sys).setBroken()
	panic(reason)
}

func (s *SystemInitConstraint) isValid() bool {
	return *s.sys == nil
}

type ISystem interface {
	Type() reflect.Type
	Order() Order
	World() IWorld
	GetRequirements() map[reflect.Type]IRequirement
	IsRequire(component IComponent) bool
	ID() int64
	GetUtility() IUtility

	pause()
	resume()
	stop()
	getPointer() unsafe.Pointer
	isRequire(componentType reflect.Type) bool
	setOrder(order Order)
	setRequirements(initializer SystemInitConstraint, rqs ...IRequirement)
	getState() SystemState
	setState(state SystemState)
	setSecurity(isSafe bool)
	isThreadSafe() bool
	setExecuting(isExecuting bool)
	isExecuting() bool
	baseInit(world *ecsWorld, ins ISystem)
	getOptimizer() *OptimizerReporter
	getGetterCache() *GetterCache
	setBroken()
	isValid() bool
	setUtility(u IUtility)
}

type SystemObject interface {
	__SystemIdentification()
}

type SystemPointer[T SystemObject] interface {
	ISystem
	*T
}

type systemIdentification struct{}

func (s systemIdentification) __SystemIdentification() {}

type System[T SystemObject] struct {
	systemIdentification
	lock              sync.Mutex
	requirements      map[reflect.Type]IRequirement
	getterCache       *GetterCache
	order             Order
	optimizerReporter *OptimizerReporter
	world             *ecsWorld
	utility           IUtility
	realType          reflect.Type
	state             SystemState
	valid             bool
	isSafe            bool
	executing         bool
	id                int64
}

func (s *System[T]) instance() (sys ISystem) {
	(*iface)(unsafe.Pointer(&sys)).data = unsafe.Pointer(s)
	return
}

func (s *System[T]) rawInstance() *T {
	return (*T)(unsafe.Pointer(s))
}

func (s *System[T]) ID() int64 {
	if s.id == 0 {
		s.id = LocalUniqueID()
	}
	return s.id
}

func (s *System[T]) SetRequirements(initializer SystemInitConstraint, rqs ...IRequirement) {
	if initializer.isValid() {
		panic("out of initialization stage")
	}
	s.setRequirements(initializer, rqs...)
}

func (s *System[T]) setRequirementsInternal(rqs ...IRequirement) {
	if s.requirements == nil {
		s.requirements = map[reflect.Type]IRequirement{}
	}
	var typ reflect.Type
	for _, value := range rqs {
		typ = value.Type()
		s.requirements[typ] = value
	}
}

func (s *System[T]) isInitialized() bool {
	return s.state >= SystemStateInit
}

func (s *System[T]) setRequirements(initializer SystemInitConstraint, rqs ...IRequirement) {
	if s.requirements == nil {
		s.requirements = map[reflect.Type]IRequirement{}
	}
	var typ reflect.Type
	for _, value := range rqs {
		typ = value.Type()
		value.check(initializer)
		s.requirements[typ] = value
		s.World().getComponentMetaInfoByType(typ)
	}
}

func (s *System[T]) setUtility(u IUtility) {
	s.utility = u
}

func (s *System[T]) setSecurity(isSafe bool) {
	s.isSafe = isSafe
}
func (s *System[T]) isThreadSafe() bool {
	return s.isSafe
}

func (s *System[T]) GetUtility() IUtility {
	return s.utility
}

func (s *System[T]) pause() {
	if s.getState() == SystemStateUpdate {
		s.setState(SystemStatePause)
	}
}

func (s *System[T]) resume() {
	if s.getState() == SystemStatePause {
		s.setState(SystemStateUpdate)
	}
}

func (s *System[T]) stop() {
	if s.getState() < SystemStateDestroy {
		s.setState(SystemStateDestroy)
	}
}

func (s *System[T]) getState() SystemState {
	return s.state
}

func (s *System[T]) setState(state SystemState) {
	s.state = state
}

func (s *System[T]) setBroken() {
	s.valid = false
}

func (s *System[T]) isValid() bool {
	return s.valid
}

func (s *System[T]) setExecuting(isExecuting bool) {
	s.executing = isExecuting
}

func (s *System[T]) isExecuting() bool {
	return s.executing
}

func (s *System[T]) GetRequirements() map[reflect.Type]IRequirement {
	return s.requirements
}

func (s *System[T]) IsRequire(com IComponent) bool {
	return s.isRequire(com.Type())
}

func (s *System[T]) isRequire(typ reflect.Type) bool {
	_, ok := s.requirements[typ]
	return ok
}

func (s *System[T]) baseInit(world *ecsWorld, ins ISystem) {
	s.requirements = map[reflect.Type]IRequirement{}
	s.getterCache = NewGetterCache(len(s.requirements))

	if ins.Order() == OrderInvalid {
		s.setOrder(OrderDefault)
	}
	s.world = world

	s.valid = true

	initializer := SystemInitConstraint{}
	is := ISystem(s)
	initializer.sys = &is
	if i, ok := ins.(InitReceiver); ok {
		err := TryAndReport(func() error {
			return i.Init(initializer)
		})
		if err != nil {
			Log.Error(err)
		}
	}
	*initializer.sys = nil
	initializer.sys = nil

	s.state = SystemStateStart
}

func (s *System[T]) getPointer() unsafe.Pointer {
	return unsafe.Pointer(s)
}

func (s *System[T]) Type() reflect.Type {
	if s.realType == nil {
		s.realType = TypeOf[T]()
	}
	return s.realType
}

func (s *System[T]) setOrder(order Order) {
	if s.isInitialized() {
		return
	}

	s.order = order
}

func (s *System[T]) Order() Order {
	return s.order
}

func (s *System[T]) World() IWorld {
	return s.world
}

func (s *System[T]) GetEntityInfo(entity Entity) (*EntityInfo, bool) {
	return s.world.getEntityInfo(entity)
}

// get optimizer
func (s *System[T]) getOptimizer() *OptimizerReporter {
	if s.optimizerReporter == nil {
		s.optimizerReporter = &OptimizerReporter{}
		s.optimizerReporter.init()
	}
	return s.optimizerReporter
}

func (s *System[T]) getGetterCache() *GetterCache {
	return s.getterCache
}


================================================
FILE: system_event.go
================================================
package ecs

import "time"

type Event struct {
	Frame uint64
	Delta time.Duration
}

type InitReceiver interface {
	Init(initializer SystemInitConstraint) error
}

type SyncBeforeStartReceiver interface {
	SyncBeforeStart(event Event)
}

type StartReceiver interface {
	Start(event Event)
}

type SyncAfterStartReceiver interface {
	SyncAfterStart(event Event)
}

type SyncBeforePreUpdateReceiver interface {
	SyncBeforePreUpdate(event Event)
}

type PreUpdateReceiver interface {
	PreUpdate(event Event)
}

type SyncAfterPreUpdateReceiver interface {
	SyncAfterPreUpdate(event Event)
}

type SyncBeforeUpdateReceiver interface {
	SyncBeforeUpdate(event Event)
}

type UpdateReceiver interface {
	Update(event Event)
}

type SyncAfterUpdateReceiver interface {
	SyncAfterUpdate(event Event)
}

type SyncBeforePostUpdateReceiver interface {
	SyncBeforePostUpdate(event Event)
}

type PostUpdateReceiver interface {
	PostUpdate(event Event)
}

type SyncAfterPostUpdateReceiver interface {
	SyncAfterPostUpdate(event Event)
}

type SyncBeforeDestroyReceiver interface {
	SyncBeforeDestroy(event Event)
}

type DestroyReceiver interface {
	Destroy(event Event)
}

type SyncAfterPostDestroyReceiver interface {
	SyncAfterDestroy(event Event)
}


================================================
FILE: system_flow.go
================================================
package ecs

import (
	"fmt"
	"reflect"
	"sync"
)

const (
	StageSyncBeforeStart Stage = iota
	StageStart
	StageSyncAfterStart

	StageSyncBeforePreUpdate
	StagePreUpdate
	StageSyncAfterPreUpdate

	StageSyncBeforeUpdate
	StageUpdate
	StageSyncAfterUpdate

	StageSyncBeforePostUpdate
	StagePostUpdate
	StageSyncAfterPostUpdate

	StageSyncBeforeDestroy
	StageDestroy
	StageSyncAfterDestroy
)

// Stage system execute period:start->pre_update->update->pre_destroy->destroy
type Stage uint32

// Order default suborder of system
type Order int32

const (
	OrderFront   Order = -1
	OrderInvalid Order = 0
	OrderAppend  Order = 99999999
	OrderDefault Order = OrderAppend
)

// SystemGroupList extension of system group slice
type SystemGroupList []*SystemGroup

// system execute flow
type systemFlow struct {
	world     *ecsWorld
	stages    map[Stage]SystemGroupList
	stageList []Stage
	systems   map[reflect.Type]ISystem
	wg        *sync.WaitGroup
}

func newSystemFlow(runtime *ecsWorld) *systemFlow {
	sf := &systemFlow{
		world:   runtime,
		systems: map[reflect.Type]ISystem{},
		wg:      &sync.WaitGroup{},
	}
	sf.init()
	return sf
}

// initialize the system flow
func (p *systemFlow) init() {
	p.stageList = []Stage{
		StageSyncBeforeStart,
		StageStart,
		StageSyncAfterStart,

		StageSyncBeforePreUpdate,
		StagePreUpdate,
		StageSyncAfterPreUpdate,

		StageSyncBeforeUpdate,
		StageUpdate,
		StageSyncAfterUpdate,

		StageSyncBeforePostUpdate,
		StagePostUpdate,
		StageSyncAfterPostUpdate,

		StageSyncBeforeDestroy,
		StageDestroy,
		StageSyncAfterDestroy,
	}
	p.reset()
}

func (p *systemFlow) reset() {
	p.stages = make(map[Stage]SystemGroupList)
	for _, value := range p.stageList {
		p.stages[value] = SystemGroupList{}
		sgFront := NewSystemGroup()
		sgFront.order = OrderFront
		sgAppend := NewSystemGroup()
		sgAppend.order = OrderAppend
		p.stages[value] = append(p.stages[value], sgFront, sgAppend)
	}
}

func (p *systemFlow) flushTempTask() {
	tasks := p.world.components.getTempTasks()
	p.wg.Add(len(tasks))
	for _, task := range tasks {
		wg := p.wg
		fn := task
		p.world.addJob(func() {
			fn()
			wg.Done()
		})
	}
	p.wg.Wait()
}

func (p *systemFlow) systemUpdate(event Event) {
	var sq SystemGroupList
	var sys ISystem
	var imp bool = false
	var runSync bool = false
	var fn func(event Event)
	for _, period := range p.stageList {
		sq = p.stages[period]
		for _, sl := range sq {
			if sl.systemCount() == 0 {
				continue
			}
			for ss := sl.Begin(); !sl.End(); ss = sl.Next() {
				if systemCount := len(ss); systemCount != 0 {
					for i := 0; i < systemCount; i++ {
						sys = ss[i]

						if !sys.isValid() {
							continue
						}

						imp = false
						runSync = false
						state := ss[i].getState()

						if period > StageSyncAfterStart {
							if state == SystemStateStart {
								state = SystemStateUpdate
								sys.setState(SystemStateUpdate)
							}
						}

						if state == SystemStateStart {
							if period > StageSyncAfterStart {
								continue
							}
							switch period {
							case StageSyncBeforeStart:
								system, ok := sys.(SyncBeforeStartReceiver)
								fn = system.SyncBeforeStart
								imp = ok
								runSync = true
							case StageStart:
								system, ok := sys.(StartReceiver)
								fn = system.Start
								imp = ok
								runSync = false
							case StageSyncAfterStart:
								system, ok := sys.(SyncAfterStartReceiver)
								fn = system.SyncAfterStart
								imp = ok
								runSync = true
							}
						} else if state == SystemStateUpdate {
							if period < StageSyncBeforePreUpdate || period > StageSyncAfterPostUpdate {
								continue
							}
							switch period {
							case StageSyncBeforePreUpdate:
								system, ok := sys.(SyncBeforePreUpdateReceiver)
								fn = system.SyncBeforePreUpdate
								imp = ok
								runSync = true
							case StagePreUpdate:
								system, ok := sys.(PreUpdateReceiver)
								fn = system.PreUpdate
								imp = ok
								runSync = true
							case StageSyncAfterPreUpdate:
								system, ok := sys.(SyncAfterPreUpdateReceiver)
								fn = system.SyncAfterPreUpdate
								imp = ok
								runSync = true

							case StageSyncBeforeUpdate:
								system, ok := sys.(SyncBeforeUpdateReceiver)
								fn = system.SyncBeforeUpdate
								imp = ok
								runSync = true
							case StageUpdate:
								system, ok := sys.(UpdateReceiver)
								fn = system.Update
								imp = ok
								runSync = false
							case StageSyncAfterUpdate:
								system, ok := sys.(SyncAfterUpdateReceiver)
								fn = system.SyncAfterUpdate
								imp = ok
								runSync = true

							case StageSyncBeforePostUpdate:
								system, ok := sys.(SyncBeforePostUpdateReceiver)
								fn = system.SyncBeforePostUpdate
								imp = ok
								runSync = true
							case StagePostUpdate:
								system, ok := sys.(PostUpdateReceiver)
								fn = system.PostUpdate
								imp = ok
								runSync = false
							case StageSyncAfterPostUpdate:
								system, ok := sys.(SyncAfterPostUpdateReceiver)
								fn = system.SyncAfterPostUpdate
								imp = ok
								runSync = true
							}
						} else if state == SystemStateDestroy {
							if period < StageSyncBeforeDestroy {
								continue
							}
							switch period {
							case StageSyncBeforeDestroy:
								system, ok := sys.(SyncBeforeDestroyReceiver)
								fn = system.SyncBeforeDestroy
								imp = ok
								runSync = true
							case StageDestroy:
								system, ok := sys.(DestroyReceiver)
								fn = system.Destroy
								imp = ok
								runSync = false
							case StageSyncAfterDestroy:
								system, ok := sys.(SyncAfterPostDestroyReceiver)
								fn = system.SyncAfterDestroy
								imp = ok
								runSync = true

								sys.setState(SystemStateDestroyed)
							}
						}

						if !imp {
							continue
						}
						if runSync {
							sys.setExecuting(true)
							sys.setSecurity(true)
							fn(event)
							sys.setSecurity(false)
							sys.setExecuting(false)
						} else {
							wrapper := func(fn func(event2 Event), e Event) func() {
								sys.setExecuting(true)
								return func() {
									defer func() {
										sys.setExecuting(false)
										p.wg.Done()
									}()
									fn(e)
								}
							}
							p.wg.Add(1)
							p.world.addJob(wrapper(fn, event))
						}
					}
				}
				p.wg.Wait()
			}
		}
	}
}

func (p *systemFlow) run(event Event) {
	reporter := p.world.metrics.NewReporter("system_flow_run")
	reporter.Start()

	//Log.Info("system flow # Temp Task Execute #")
	p.flushTempTask()
	reporter.Sample("Temp Task Execute")

	//Log.Info("system flow # Logic #")
	p.systemUpdate(event)
	reporter.Sample("system execute")

	//Log.Info("system flow # Clear Disposable #")
	p.world.components.clearDisposable()
	reporter.Sample("Clear Disposable")

	p.flushTempTask()
	reporter.Sample("Temp Task Execute")

	reporter.Stop()
	reporter.Print()
}

// register method only in world init or func init(){}
func (p *systemFlow) register(system ISystem) {
	if p.world.getStatus() != WorldStatusInitialized {
		panic("system register only in world init")
	}

	//init function call
	system.baseInit(p.world, system)

	order := system.Order()
	if order > OrderAppend {
		Log.Errorf("system order must less then %d, resort order to %d", OrderAppend+1, OrderAppend)
		order = OrderAppend
	}

	for _, period := range p.stageList {

		if !p.isImpEvent(system, period) {
			continue
		}

		sl := p.stages[period]
		if order == OrderFront {
			p.stages[period][0].insert(system)
		} else if order == OrderAppend {
			p.stages[period][len(sl)-1].insert(system)
		} else {
			for i, v := range sl {
				if order == v.order {
					v.insert(system)
					break
				} else if order < v.order {
					sg := NewSystemGroup()
					sg.order = order
					sg.insert(system)
					temp := append(SystemGroupList{}, sl[i-1:]...)
					p.stages[period] = append(append(sl[:i-1], sg), temp...)
					break
				}
			}
		}
	}

	p.systems[system.Type()] = system
}

func (p *systemFlow) isImpEvent(system ISystem, period Stage) bool {
	imp := false
	switch period {
	case StageSyncBeforeStart:
		_, imp = system.(SyncBeforeStartReceiver)
	case StageStart:
		_, imp = system.(StartReceiver)
	case StageSyncAfterStart:
		_, imp = system.(SyncAfterStartReceiver)
	case StageSyncBeforePreUpdate:
		_, imp = system.(SyncBeforePreUpdateReceiver)
	case StagePreUpdate:
		_, imp = system.(PreUpdateReceiver)
	case StageSyncAfterPreUpdate:
		_, imp = system.(SyncAfterPreUpdateReceiver)
	case StageSyncBeforeUpdate:
		_, imp = system.(SyncBeforeUpdateReceiver)
	case StageUpdate:
		_, imp = system.(UpdateReceiver)
	case StageSyncAfterUpdate:
		_, imp = system.(SyncAfterUpdateReceiver)
	case StageSyncBeforePostUpdate:
		_, imp = system.(SyncBeforePostUpdateReceiver)
	case StagePostUpdate:
		_, imp = system.(PostUpdateReceiver)
	case StageSyncAfterPostUpdate:
		_, imp = system.(SyncAfterPostUpdateReceiver)
	case StageSyncBeforeDestroy:
		_, imp = system.(SyncBeforeDestroyReceiver)
	case StageDestroy:
		_, imp = system.(DestroyReceiver)
	case StageSyncAfterDestroy:
		_, imp = system.(SyncAfterPostDestroyReceiver)
	}
	return imp
}

func (p *systemFlow) stop() {
	p.reset()
}

func (p *systemFlow) SystemInfoPrint() {
	m := map[Stage]string{
		StageSyncBeforeStart: "StageSyncBeforeStart",
		StageStart:           "StageStart",
		StageSyncAfterStart:  "StageSyncAfterStart",

		StageSyncBeforePreUpdate: "StageSyncBeforePreUpdate",
		StagePreUpdate:           "StagePreUpdate",
		StageSyncAfterPreUpdate:  "StageSyncAfterPreUpdate",

		StageSyncBeforeUpdate: "StageSyncBeforeUpdate",
		StageUpdate:           "StageUpdate",
		StageSyncAfterUpdate:  "StageSyncAfterUpdate",

		StageSyncBeforePostUpdate: "StageSyncBeforePostUpdate",
		StagePostUpdate:           "StagePostUpdate",
		StageSyncAfterPostUpdate:  "StageSyncAfterPostUpdate",

		StageSyncBeforeDestroy: "StageSyncBeforeDestroy",
		StageDestroy:           "StageDestroy",
		StageSyncAfterDestroy:  "StageSyncAfterDestroy",
	}
	Log.Infof("┌──────────────── # System Info # ─────────────────")
	Log.Infof("├─ Total: %d", len(p.systems))

	var output []string
	var sq SystemGroupList
	for pi, period := range p.stageList {
		var slContent []string
		sq = p.stages[period]
		for i, sl := range sq {
			sl.resort()
			batchTotal := sl.batchCount()
			batch := 0
			var batchContent []string
			for ss := sl.Begin(); !sl.End(); ss = sl.Next() {
				if systemCount := len(ss); systemCount != 0 {

					str := "│     │  └─ "
					if batch == batchTotal-1 {
						str = "│        └─ "
					}
					for i := 0; i < systemCount; i++ {
						str += fmt.Sprintf("%s ", ss[i].Type().Name())
					}
					if batch == batchTotal-1 {
						batchContent = append(batchContent, fmt.Sprintf("│     └─ Batch %d", batch))
					} else {
						batchContent = append(batchContent, fmt.Sprintf("│     ├─ Batch %d", batch))
					}
					batchContent = append(batchContent, str)
				}
				batch++
			}
			if len(batchContent) > 0 {
				s := make([]string, 0, len(batchContent)+1)
				if i == len(sq)-1 {
					s = append(s, fmt.Sprintf("│  └─ Order %d", i))
				} else {
					s = append(s, fmt.Sprintf("│  ├─ Order %d", i))
				}
				s = append(s, batchContent...)
				slContent = append(slContent, s...)
			}
		}
		if len(slContent) > 0 {
			s := make([]string, 0, len(slContent)+1)
			if pi == len(p.stageList)-1 {
				s = append(s, fmt.Sprintf("└─ Stage %s", m[period]))
			} else {
				s = append(s, fmt.Sprintf("├─ Stage %s", m[period]))
			}
			s = append(s, slContent...)
			output = append(output, s...)
		}
	}

	for _, v := range output {
		Log.Info(v)
	}
	Log.Infof("└────────────── # System Info End # ───────────────")
}


================================================
FILE: system_group.go
================================================
package ecs

import (
	"reflect"
	"sort"
)

var emptySystemGroupIterator = &SystemGroupIterator{}

// Node system tree node
type Node struct {
	parent   *Node
	children []*Node
	val      ISystem
}

func (p *Node) isFriend(node *Node) bool {
	for com, r := range p.val.GetRequirements() {
		for comTarget, rTarget := range node.val.GetRequirements() {
			if comTarget == com {
				if r.getPermission() == ComponentReadOnly && rTarget.getPermission() == ComponentReadOnly {
					continue
				}
				return true
			}
		}
	}
	return false
}

func (p *Node) attach(node *Node) {
	isAttached := false
	for i := 0; i < len(p.children); i++ {
		if p.children[i].isFriend(node) {
			p.children[i].attach(node)
			isAttached = true
			break
		}
	}
	if !isAttached {
		if p.val == node.val {
			Log.Error("repeated system")
			return
		}
		p.children = append(p.children, node)
	}
}

// SystemGroup system group ordered by interrelation
type SystemGroup struct {
	SystemGroupIterator
	systems      []*Node
	ref          map[reflect.Type]int
	root         *Node
	order        Order
	batchTotal   int
	maxPeerBatch int
	ordered      bool
}

func NewSystemGroup() *SystemGroup {
	return &SystemGroup{
		systems: make([]*Node, 0),
		ref:     map[reflect.Type]int{},
		ordered: true,
		root: &Node{
			parent:   nil,
			children: []*Node{},
			val:      nil,
		},
	}
}

func (p *SystemGroup) refCount(rqs map[reflect.Type]IRequirement) int {
	ref := 0
	for com, _ := range rqs {
		ref += p.ref[com] - 1
	}
	return ref
}

func (p *SystemGroup) resort() {
	if p.ordered {
		return
	}
	sort.Slice(p.systems, func(i, j int) bool {
		return p.refCount(p.systems[i].val.GetRequirements()) >
			p.refCount(p.systems[j].val.GetRequirements())
	})

	p.root.children = []*Node{}
	for _, node := range p.systems {
		node.children = []*Node{}
		p.root.attach(node)
	}
	p.ordered = true

	p.batchTotal = 0
	p.maxPeerBatch = 0

	var top []*Node = p.root.children
	for len(top) > 0 {
		count := 0
		temp := top
		top = make([]*Node, 0)
		for _, node := range temp {
			count++
			top = append(top, node.children...)
		}
		if count > p.maxPeerBatch {
			p.maxPeerBatch = count
		}
		p.batchTotal++
	}

	p.resetIter()
}

func (p *SystemGroup) resetIter() {
	if p.group == nil {
		p.group = p
	}
	curLen := len(p.SystemGroupIterator.top)
	if curLen-p.maxPeerBatch == 1 {
		p.SystemGroupIterator.top = p.SystemGroupIterator.top[:p.maxPeerBatch]
		p.SystemGroupIterator.topTemp = p.SystemGroupIterator.topTemp[:p.maxPeerBatch]
		p.SystemGroupIterator.buffer = p.SystemGroupIterator.buffer[:p.maxPeerBatch]
	} else if curLen-p.maxPeerBatch == -1 {
		p.SystemGroupIterator.top = append(p.SystemGroupIterator.top, (*Node)(nil))
		p.SystemGroupIterator.topTemp = append(p.SystemGroupIterator.topTemp, (*Node)(nil))
		p.SystemGroupIterator.buffer = append(p.SystemGroupIterator.buffer, ISystem(nil))
	} else if curLen-p.maxPeerBatch == 0 {
		// do nothing
	} else {
		p.SystemGroupIterator.top = make([]*Node, p.maxPeerBatch)
		p.SystemGroupIterator.topTemp = make([]*Node, p.maxPeerBatch)
		p.SystemGroupIterator.buffer = make([]ISystem, p.maxPeerBatch)
	}
}

func (p *SystemGroup) systemCount() int {
	return len(p.systems)
}

func (p *SystemGroup) batchCount() int {
	return p.batchTotal
}

func (p *SystemGroup) maxSystemCountPeerBatch() int {
	return p.maxPeerBatch
}

// get all systems
func (p *SystemGroup) all() []ISystem {
	systems := make([]ISystem, len(p.systems))
	for i, n := range p.systems {
		systems[i] = n.val
	}
	return systems
}

// insert system
func (p *SystemGroup) insert(sys ISystem) {
	//get system's required components
	rqs := sys.GetRequirements()
	if len(rqs) == 0 {
		//panic("invalid system")
	}
	//reference count
	for com, _ := range rqs {
		if _, ok := p.ref[com]; ok {
			p.ref[com] += 1
		} else {
			p.ref[com] = 1
		}
	}
	//add system
	node := &Node{
		children: make([]*Node, 0),
		val:      sys,
	}
	p.systems = append(p.systems, node)
	//set unordered
	p.ordered = false
}

// has system
func (p *SystemGroup) has(sys ISystem) bool {
	for _, system := range p.systems {
		if system.val.Type() == sys.Type() {
			return true
		}
	}
	return false
}

// remove system
func (p *SystemGroup) remove(sys ISystem) {
	//get system's required components
	rqs := sys.GetRequirements()
	has := false
	for i, system := range p.systems {
		if system.val.ID() == sys.ID() {
			p.systems = append(p.systems[:i], p.systems[i+1:]...)
			has = true
			break
		}
	}
	if !has {
		return
	}
	//reference count
	for com, _ := range rqs {
		if _, ok := p.ref[com]; ok {
			p.ref[com] -= 1
		} else {
			panic("component ref wrong")
		}
	}
	//set unordered
	p.ordered = false
}

func (p *SystemGroup) iter() *SystemGroupIterator {
	if !p.ordered {
		p.resort()
	}

	if p.maxPeerBatch == 0 {
		return emptySystemGroupIterator
	}

	return &SystemGroupIterator{
		group:   p,
		top:     make([]*Node, p.maxPeerBatch),
		topTemp: make([]*Node, p.maxPeerBatch),
		buffer:  make([]ISystem, p.maxPeerBatch),
	}
}

type SystemGroupIterator struct {
	group   *SystemGroup
	top     []*Node
	topTemp []*Node
	buffer  []ISystem
	topSize int
	size    int
}

func (s *SystemGroupIterator) Begin() []ISystem {
	if s.group == nil {
		return nil
	}
	if !s.group.ordered {
		s.group.resort()
	}
	copy(s.top, s.group.root.children)
	s.topSize = len(s.group.root.children)
	return s.Next()
}

func (s *SystemGroupIterator) Next() []ISystem {
	if s.topSize == 0 {
		s.size = 0
		return nil
	}
	s.topTemp, s.top = s.top, s.topTemp
	tempSize := s.topSize
	s.topSize = 0
	s.size = 0
	for i := 0; i < tempSize; i++ {
		s.buffer[s.size] = s.topTemp[i].val
		s.size++
		for j := 0; j < len(s.topTemp[i].children); j++ {
			s.top[s.topSize+j] = s.topTemp[i].children[j]
		}
		s.topSize += len(s.topTemp[i].children)
	}

	if s.size == 0 {
		return nil
	}
	return s.buffer[:s.size]
}

func (s *SystemGroupIterator) End() bool {
	return s.size == 0
}


================================================
FILE: system_group_test.go
================================================
package ecs

import (
	"testing"
)

type __systemGroup_Test_C_1 struct {
	Component[__systemGroup_Test_C_1]
}
type __systemGroup_Test_C_2 struct {
	Component[__systemGroup_Test_C_2]
}
type __systemGroup_Test_C_3 struct {
	Component[__systemGroup_Test_C_3]
}
type __systemGroup_Test_C_4 struct {
	Component[__systemGroup_Test_C_4]
}
type __systemGroup_Test_C_5 struct {
	Component[__systemGroup_Test_C_5]
}
type __systemGroup_Test_C_6 struct {
	Component[__systemGroup_Test_C_6]
}
type __systemGroup_Test_C_7 struct {
	Component[__systemGroup_Test_C_7]
}
type __systemGroup_Test_C_8 struct {
	Component[__systemGroup_Test_C_8]
}
type __systemGroup_Test_C_9 struct {
	Component[__systemGroup_Test_C_9]
}
type __systemGroup_Test_C_10 struct {
	Component[__systemGroup_Test_C_10]
}

type __systemGroup_Test_S_1 struct {
	System[__systemGroup_Test_S_1]
	Name int
}

func NewTestSystem(ID int, rqs ...IRequirement) *__systemGroup_Test_S_1 {
	s := &__systemGroup_Test_S_1{Name: ID}
	s.setRequirementsInternal(rqs...)
	return s
}

func (p *__systemGroup_Test_S_1) Call(label int) interface{} {
	switch label {
	case 1:
		println(p.Name)
	}
	return nil
}

func newSystemGroupTestSystem() []ISystem {
	return []ISystem{
		NewTestSystem(1, &__systemGroup_Test_C_1{}, &__systemGroup_Test_C_2{}),
		NewTestSystem(2, &ReadOnly[__systemGroup_Test_C_1]{}, &__systemGroup_Test_C_3{}),
		NewTestSystem(3, &__systemGroup_Test_C_2{}, &__systemGroup_Test_C_5{}),
		NewTestSystem(4, &__systemGroup_Test_C_2{}, &__systemGroup_Test_C_3{}, &__systemGroup_Test_C_6{}),
		NewTestSystem(5, &__systemGroup_Test_C_7{}),
		NewTestSystem(6, &__systemGroup_Test_C_9{}, &__systemGroup_Test_C_10{}),
		NewTestSystem(7, &__systemGroup_Test_C_6{}),
		NewTestSystem(8, &__systemGroup_Test_C_1{}, &__systemGroup_Test_C_5{}),
		NewTestSystem(9, &__systemGroup_Test_C_4{}, &__systemGroup_Test_C_6{}),
		NewTestSystem(10, &__systemGroup_Test_C_7{}, &__systemGroup_Test_C_5{}),
		NewTestSystem(11, &ReadOnly[__systemGroup_Test_C_1]{}),
	}
}

func TestNewSystemGroupIterEmpty(t *testing.T) {
	sg := NewSystemGroup()
	sg.resort()

	Log.Infof("========== system count %d, Batch count: %d, Max peer Batch: %d:", sg.systemCount(), sg.batchCount(), sg.maxSystemCountPeerBatch())

	for ss := sg.Begin(); !sg.End(); ss = sg.Next() {
		Log.Info("========== batch:")
		for _, s := range ss {
			Log.Infof("%s\n", ObjectToString(s))
		}
	}
}

func TestNewSystemGroupIter(t *testing.T) {
	tests := newSystemGroupTestSystem()
	sg := NewSystemGroup()
	for _, test := range tests {
		sg.insert(test)
	}

	sg.resort()

	Log.Infof("========== system count %d, Batch count: %d, Max peer Batch: %d:", sg.systemCount(), sg.batchCount(), sg.maxSystemCountPeerBatch())

	for ss := sg.Begin(); !sg.End(); ss = sg.Next() {
		Log.Info("========== batch:")
		for _, s := range ss {
			Log.Infof("%s\n", ObjectToString(s))
		}
	}
}

func TestNewSystemGroupIterTemp(t *testing.T) {
	tests := newSystemGroupTestSystem()
	sg := NewSystemGroup()
	for _, test := range tests {
		sg.insert(test)
	}

	sg.resort()

	Log.Infof("========== system count %d, Batch count: %d, Max peer Batch: %d:", sg.systemCount(), sg.batchCount(), sg.maxSystemCountPeerBatch())

	for ss := sg.Begin(); !sg.End(); ss = sg.Next() {
		Log.Info("========== batch:")
		for _, s := range ss {
			Log.Infof("%s\n", ObjectToString(s))
		}
	}

	for ss := sg.Begin(); !sg.End(); ss = sg.Next() {
		Log.Info("========== batch:")
		for _, s := range ss {
			Log.Infof("%s\n", ObjectToString(s))
		}
	}
}

func BenchmarkSystemGroupIter(b *testing.B) {
	tests := newSystemGroupTestSystem()
	sg := NewSystemGroup()
	for _, test := range tests {
		sg.insert(test)
	}

	sg.resort()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		for ss := sg.Begin(); !sg.End(); ss = sg.Next() {
			_ = ss
		}
	}
}


================================================
FILE: system_requirement.go
================================================
package ecs

import (
	"reflect"
	"unsafe"
)

const (
	ComponentReadWrite ComponentPermission = 0
	ComponentReadOnly  ComponentPermission = 1
)

type ComponentPermission uint8

type IRequirement interface {
	Type() reflect.Type
	getPermission() ComponentPermission
	check(initializer SystemInitConstraint)
}

type ReadOnly[T ComponentObject] struct{}

func (r *ReadOnly[T]) Type() reflect.Type {
	return TypeOf[T]()
}

func (r *ReadOnly[T]) getPermission() ComponentPermission {
	return ComponentReadOnly
}

func (r *ReadOnly[T]) check(initializer SystemInitConstraint) {
	ins := any((*T)(unsafe.Pointer(r))).(IComponent)
	ins.check(initializer)
}


================================================
FILE: unordered_collection.go
================================================
package ecs

import (
	"unsafe"
)

const (
	InitMaxSize = 1024 * 16
	//InitMaxSize        = 0
	SeqMax uint32 = 0xFFFFFFFF
)

type UnorderedCollection[T any] struct {
	eleSize  uintptr
	len      int64
	initSize int64
	data     []T
}

func NewUnorderedCollection[T any](initSize ...int) *UnorderedCollection[T] {
	typ := TypeOf[T]()
	eleSize := typ.Size()
	size := InitMaxSize / eleSize
	c := &UnorderedCollection[T]{
		data: make([]T, 0, size),
	}
	if len(initSize) > 0 {
		c.initSize = int64(initSize[0])
		c.eleSize = uintptr(initSize[0]) / eleSize
	}
	return c
}

func (c *UnorderedCollection[T]) Get(idx int64) *T {
	return (*T)(unsafe.Add(unsafe.Pointer(&c.data[0]), uintptr(idx)*c.eleSize))
}

func (c *UnorderedCollection[T]) Add(element *T) (*T, int64) {
	if int64(len(c.data)) > c.len {
		c.data[c.len] = *element
	} else {
		c.data = append(c.data, *element)
	}
	idx := c.len
	c.len++
	return &c.data[idx], idx
}

func (c *UnorderedCollection[T]) Remove(idx int64) (*T, int64, int64) {
	if idx < 0 {
		return nil, 0, 0
	}
	lastIdx := c.len - 1

	c.data[idx], c.data[lastIdx] = c.data[lastIdx], c.data[idx]
	c.shrink()
	c.len--
	removed := c.data[lastIdx]
	return &removed, lastIdx, idx
}

func (c *UnorderedCollection[T]) Len() int {
	return int(c.len)
}

func (c *UnorderedCollection[T]) Range(f func(element *T) bool) {
	for i := int64(0); i < c.len; i++ {
		if !f(&c.data[i]) {
			break
		}
	}
}

func (c *UnorderedCollection[T]) Clear() {
	c.data = make([]T, 0, c.initSize)
	c.len = 0
}

func (c *UnorderedCollection[T]) shrink() {
	var threshold int64
	if len(c.data) < InitMaxSize {
		return
	} else {
		threshold = int64(float64(c.len) * 1.25)
	}
	if int64(len(c.data)) > threshold {
		//c.data = c.data[:threshold]
		newData := make([]T, threshold)
		copy(newData, c.data)
		c.data = newData
	}
}

func (c *UnorderedCollection[T]) getIndexByElePointer(element *T) int64 {
	if c.len == 0 {
		return -1
	}
	offset := uintptr(unsafe.Pointer(element)) - uintptr(unsafe.Pointer(&c.data[0]))
	if offset%c.eleSize != 0 {
		return -1
	}
	idx := int64(offset / c.eleSize)
	if idx < 0 || idx > c.len-1 {
		return -1
	}
	return idx
}

func NewUnorderedCollectionIterator[T ComponentObject](collection *UnorderedCollection[T], readOnly ...bool) Iterator[T] {
	iter := &Iter[T]{
		data:    collection.data,
		len:     collection.Len(),
		eleSize: collection.eleSize,
		offset:  0,
	}
	if len(readOnly) > 0 {
		iter.readOnly = readOnly[0]
	}
	if iter.len != 0 {
		iter.head = unsafe.Pointer(&collection.data[0])
		if iter.readOnly {
			iter.curTemp = collection.data[0]
			iter.cur = &iter.curTemp
		} else {
			iter.cur = &(collection.data[0])
		}
	}

	return iter
}


================================================
FILE: unordered_collection_test.go
================================================
package ecs

import (
	"testing"
	"unsafe"
)

type __unorderedCollection_Test_item struct {
	Component[__unorderedCollection_Test_item]
	ItemID int64
	Arr    [3]int
}

func TestUnorderedCollection_Iterator(t *testing.T) {
	//准备数据
	caseCount := 50
	var srcList []__unorderedCollection_Test_item
	for i := 0; i < caseCount; i++ {
		srcList = append(srcList, __unorderedCollection_Test_item{
			ItemID: int64(i),
			Arr:    [3]int{1, 2, 3},
		})
	}

	//创建容器(无序数据集)
	c := NewUnorderedCollection[__unorderedCollection_Test_item]()

	//添加数据
	for i := 0; i < caseCount; i++ {
		_, _ = c.Add(&srcList[i])
	}

	//遍历风格 1:
	for iter := NewUnorderedCollectionIterator(c); !iter.End(); iter.Next() {
		v := iter.Val()
		_ = v
		//t.Logf("style 1: %+v\n", v)
	}

	//遍历风格 2:
	iter := NewUnorderedCollectionIterator(c)
	for c := iter.Begin(); !iter.End(); c = iter.Next() {
		_ = c
		//t.Logf("style 2: %+v\n", c)
	}
}

func BenchmarkUnorderedCollection_SliceIter(b *testing.B) {
	var slice []__unorderedCollection_Test_item
	// collection 有ID生成,此处用通常方式模拟
	var id2index = map[int]int{}

	var ids []int64
	total := 100000
	for n := 0; n < total; n++ {
		item := &__unorderedCollection_Test_item{
			ItemID: int64(n),
		}
		slice = append(slice, *item)
		id2index[n] = n
		ids = append(ids, int64(n))
	}

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		for i := 0; i < 10000; i++ {
			_ = slice[i]
		}
	}
}

func BenchmarkUnorderedCollection_Write(b *testing.B) {
	c := NewUnorderedCollection[__unorderedCollection_Test_item]()
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		item := &__unorderedCollection_Test_item{
			ItemID: int64(n),
		}
		ret, _ := c.Add(item)
		_ = ret
	}
}

func BenchmarkUnorderedCollection_SliceRead(b *testing.B) {
	total := 100000
	c := NewUnorderedCollection[__unorderedCollection_Test_item]()
	arr := make([]__unorderedCollection_Test_item, total)
	for n := 0; n < total; n++ {
		item := __unorderedCollection_Test_item{
			ItemID: int64(n),
		}
		_, _ = c.Add(&item)
		arr = append(arr, item)
	}

	fn := func(idx int64) *__unorderedCollection_Test_item {
		return nil
	}

	b.ResetTimer()

	b.Run("slice", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = arr[n%total]
		}
	})
	b.Run("unordered collection", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = c.Get(int64(n % total))
		}
	})
	b.Run("unordered collection 2", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = (*__unorderedCollection_Test_item)(unsafe.Add(unsafe.Pointer(&c.data[0]), uintptr(n%total)*c.eleSize))
		}
	})
	b.Run("unordered collection empty func", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_ = fn(int64(n % total))
		}
	})
}

func BenchmarkUnorderedCollection_Read(b *testing.B) {
	c := NewUnorderedCollection[__unorderedCollection_Test_item]()
	var ids []int64
	total := 100000
	for n := 0; n < total; n++ {
		item := &__unorderedCollection_Test_item{
			ItemID: int64(n),
		}
		_, _ = c.Add(item)
		ids = append(ids, int64(n+1))
	}

	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		_ = c.Get(int64((n + 1) % total))
	}
}

func BenchmarkUnorderedCollection_R
Download .txt
gitextract_p66ljwpu/

├── .gitignore
├── LICENSE
├── README.md
├── chunk.go
├── collection_iter.go
├── component.go
├── component_collection.go
├── component_getter.go
├── component_meta.go
├── component_meta_test.go
├── component_operate_task.go
├── component_set.go
├── component_set_test.go
├── component_test.go
├── component_utils.go
├── compound.go
├── compound_test.go
├── compound_utils.go
├── concurrent_map.go
├── concurrent_map_test.go
├── direct_api.go
├── entity.go
├── entity_info.go
├── entity_set.go
├── entity_test.go
├── example/
│   ├── benchmark-0/
│   │   ├── components.go
│   │   ├── damage_system.go
│   │   ├── game_common.go
│   │   ├── game_ecs.go
│   │   ├── game_normal.go
│   │   ├── go.mod
│   │   ├── main_benchmark_test.go
│   │   ├── main_test.go
│   │   ├── move_system.go
│   │   └── simu_load_system.go
│   └── fake-simple-game-server/
│       ├── client/
│       │   └── fake_client.go
│       ├── game/
│       │   ├── chat.go
│       │   ├── empty_system.go
│       │   ├── fake_game.go
│       │   ├── move_component.go
│       │   ├── move_system.go
│       │   ├── player_component.go
│       │   ├── position_sync_system.go
│       │   └── session.go
│       ├── gm/
│       │   └── gm.go
│       ├── go.mod
│       ├── main.go
│       └── network/
│           └── fake_net.go
├── fixed_string.go
├── fixed_string_test.go
├── fixed_string_utils.go
├── go.mod
├── go.sum
├── goroutine_id.go
├── goroutine_pool.go
├── goroutine_pool_benchmark_test.go
├── internal_type_mock.go
├── logger.go
├── metrics.go
├── optimizer.go
├── optimizer_benchmark_test.go
├── ordered_int_set.go
├── ordered_int_set_test.go
├── serialize.go
├── shape_getter.go
├── shape_getter_test.go
├── shape_iter.go
├── sparse_array.go
├── system.go
├── system_event.go
├── system_flow.go
├── system_group.go
├── system_group_test.go
├── system_requirement.go
├── unordered_collection.go
├── unordered_collection_test.go
├── unordered_collection_with_id.go
├── unordered_collection_with_id_test.go
├── utility.go
├── utils.go
├── utils_test.go
├── world.go
├── world_async.go
├── world_sync.go
└── world_test.go
Download .txt
SYMBOL INDEX (942 symbols across 78 files)

FILE: chunk.go
  constant ChunkSize (line 4) | ChunkSize uintptr = 1024 * 16
  constant EntitySize (line 6) | EntitySize uintptr = 8
  constant ChunkAddCodeSuccess (line 10) | ChunkAddCodeSuccess int = iota
  constant ChunkAddCodeFull (line 11) | ChunkAddCodeFull
  constant ChunkAddCodeInvalidElement (line 12) | ChunkAddCodeInvalidElement
  type Chunk (line 15) | type Chunk struct
  function NewChunk (line 27) | func NewChunk[T ComponentObject]() *Chunk[T] {
  method Add (line 40) | func (c *Chunk[T]) Add(element *T, id int64) (*T, int) {
  method Remove (line 55) | func (c *Chunk[T]) Remove(id int64) {
  method RemoveAndReturn (line 75) | func (c *Chunk[T]) RemoveAndReturn(id int64) *T {
  method MoveTo (line 97) | func (c *Chunk[T]) MoveTo(target *Chunk[T]) []int64 {
  method Get (line 121) | func (c *Chunk[T]) Get(id int64) *T {
  method GetByIndex (line 129) | func (c *Chunk[T]) GetByIndex(idx int64) *T {
  method Len (line 136) | func (c *Chunk[T]) Len() int64 {

FILE: collection_iter.go
  type Iterator (line 5) | type Iterator interface
  type Iter (line 12) | type Iter struct
  function EmptyIter (line 24) | func EmptyIter[T any]() Iterator[T] {
  method End (line 28) | func (i *Iter[T]) End() bool {
  method Begin (line 35) | func (i *Iter[T]) Begin() *T {
  method Val (line 48) | func (i *Iter[T]) Val() *T {
  method Next (line 52) | func (i *Iter[T]) Next() *T {

FILE: component.go
  constant h4 (line 10) | h4   = uint8(240)
  constant l4 (line 11) | l4   = uint8(15)
  constant zero (line 12) | zero = uint8(0)
  type ComponentState (line 15) | type ComponentState
  constant ComponentStateInvalid (line 18) | ComponentStateInvalid ComponentState = iota
  constant ComponentStateActive (line 19) | ComponentStateActive
  constant ComponentStateDisable (line 20) | ComponentStateDisable
  type ComponentType (line 23) | type ComponentType
  constant ComponentTypeFreeMask (line 26) | ComponentTypeFreeMask       ComponentType = 1 << 7
  constant ComponentTypeDisposableMask (line 27) | ComponentTypeDisposableMask ComponentType = 1 << 6
  constant ComponentTypeNormal (line 31) | ComponentTypeNormal         ComponentType = 0
  constant ComponentTypeDisposable (line 32) | ComponentTypeDisposable                   = 1 | ComponentTypeDisposableMask
  constant ComponentTypeFree (line 33) | ComponentTypeFree                         = 2 | ComponentTypeFreeMask
  constant ComponentTypeFreeDisposable (line 34) | ComponentTypeFreeDisposable               = 3 | ComponentTypeFreeMask | ...
  type EmptyComponent (line 37) | type EmptyComponent struct
  type IComponent (line 41) | type IComponent interface
  type ComponentObject (line 63) | type ComponentObject interface
  type FreeComponentObject (line 67) | type FreeComponentObject interface
  type DisposableComponentObject (line 71) | type DisposableComponentObject interface
  type FreeDisposableComponentObject (line 75) | type FreeDisposableComponentObject interface
  type ComponentPointer (line 79) | type ComponentPointer interface
  type FreeComponentPointer (line 84) | type FreeComponentPointer interface
  type DisposableComponentPointer (line 89) | type DisposableComponentPointer interface
  type FreeDisposableComponentPointer (line 94) | type FreeDisposableComponentPointer interface
  type FreeComponent (line 99) | type FreeComponent struct
  method getComponentType (line 103) | func (f *FreeComponent[T]) getComponentType() ComponentType {
  type DisposableComponent (line 107) | type DisposableComponent struct
  method getComponentType (line 111) | func (f *DisposableComponent[T]) getComponentType() ComponentType {
  type FreeDisposableComponent (line 115) | type FreeDisposableComponent struct
  method getComponentType (line 119) | func (f *FreeDisposableComponent[T]) getComponentType() ComponentType {
  type componentIdentification (line 123) | type componentIdentification struct
    method __ComponentIdentification (line 125) | func (c componentIdentification) __ComponentIdentification() {}
  type Component (line 127) | type Component struct
  method getComponentType (line 136) | func (c *Component[T]) getComponentType() ComponentType {
  method addToCollection (line 140) | func (c *Component[T]) addToCollection(ct ComponentType, p unsafe.Pointe...
  method deleteFromCollection (line 154) | func (c *Component[T]) deleteFromCollection(collection interface{}) {
  method newCollection (line 165) | func (c *Component[T]) newCollection(meta *ComponentMetaInfo) IComponent...
  method setOwner (line 169) | func (c *Component[T]) setOwner(entity Entity) {
  method rawInstance (line 173) | func (c *Component[T]) rawInstance() *T {
  method instance (line 177) | func (c *Component[T]) instance() IComponent {
  method setState (line 181) | func (c *Component[T]) setState(state ComponentState) {
  method getState (line 185) | func (c *Component[T]) getState() ComponentState {
  method setType (line 189) | func (c *Component[T]) setType(typ ComponentType) {
  method getType (line 193) | func (c *Component[T]) getType() ComponentType {
  method setIntType (line 197) | func (c *Component[T]) setIntType(typ uint16) {
  method getIntType (line 201) | func (c *Component[T]) getIntType() uint16 {
  method setSeq (line 205) | func (c *Component[T]) setSeq(seq uint32) {
  method getSeq (line 209) | func (c *Component[T]) getSeq() uint32 {
  method invalidate (line 213) | func (c *Component[T]) invalidate() {
  method active (line 217) | func (c *Component[T]) active() {
  method Owner (line 221) | func (c *Component[T]) Owner() Entity {
  method Type (line 225) | func (c *Component[T]) Type() reflect.Type {
  method getPermission (line 229) | func (c *Component[T]) getPermission() ComponentPermission {
  method check (line 233) | func (c *Component[T]) check(initializer SystemInitConstraint) {
  method isValidComponentType (line 246) | func (c *Component[T]) isValidComponentType() bool {
  method debugAddress (line 261) | func (c *Component[T]) debugAddress() unsafe.Pointer {
  method ToString (line 265) | func (c *Component[T]) ToString() string {

FILE: component_collection.go
  type CollectionOperate (line 9) | type CollectionOperate
  constant CollectionOperateNone (line 12) | CollectionOperateNone      CollectionOperate = iota
  constant CollectionOperateAdd (line 13) | CollectionOperateAdd
  constant CollectionOperateDelete (line 14) | CollectionOperateDelete
  constant CollectionOperateDeleteAll (line 15) | CollectionOperateDeleteAll
  type IComponentCollection (line 18) | type IComponentCollection interface
  type ComponentCollection (line 29) | type ComponentCollection struct
    method initOptTemp (line 62) | func (c *ComponentCollection) initOptTemp() {
    method operate (line 70) | func (c *ComponentCollection) operate(op CollectionOperate, entity Ent...
    method deleteOperate (line 99) | func (c *ComponentCollection) deleteOperate(op CollectionOperate, enti...
    method clearDisposable (line 128) | func (c *ComponentCollection) clearDisposable() {
    method clearFree (line 151) | func (c *ComponentCollection) clearFree() {
    method getTempTasks (line 163) | func (c *ComponentCollection) getTempTasks() []func() {
    method opExecute (line 231) | func (c *ComponentCollection) opExecute(taskList *opTaskList, collecti...
    method getComponentSet (line 256) | func (c *ComponentCollection) getComponentSet(typ reflect.Type) ICompo...
    method getComponentSetByIntType (line 261) | func (c *ComponentCollection) getComponentSetByIntType(it uint16) ICom...
    method getCollections (line 265) | func (c *ComponentCollection) getCollections() *SparseArray[uint16, IC...
    method checkSet (line 269) | func (c *ComponentCollection) checkSet(com IComponent) {
  function NewComponentCollection (line 37) | func NewComponentCollection(world *ecsWorld, k int) *ComponentCollection {

FILE: component_getter.go
  type GetterCache (line 8) | type GetterCache struct
    method Add (line 24) | func (g *GetterCache) Add(key reflect.Type, value unsafe.Pointer) {
    method Remove (line 29) | func (g *GetterCache) Remove(key reflect.Type) {
    method Get (line 46) | func (g *GetterCache) Get(key reflect.Type) unsafe.Pointer {
  function NewGetterCache (line 13) | func NewGetterCache(initCap ...int) *GetterCache {
  type ComponentGetter (line 55) | type ComponentGetter struct
  function NewComponentGetter (line 60) | func NewComponentGetter[T ComponentObject](sys ISystem) *ComponentGetter...
  method Get (line 77) | func (c *ComponentGetter[T]) Get(entity Entity) *T {

FILE: component_meta.go
  function GetComponentMeta (line 8) | func GetComponentMeta[T ComponentObject](world IWorld) *ComponentMetaInfo {
  type ComponentMetaInfo (line 12) | type ComponentMetaInfo struct
  type componentMeta (line 19) | type componentMeta struct
    method CreateComponentMetaInfo (line 39) | func (c *componentMeta) CreateComponentMetaInfo(typ reflect.Type, ct C...
    method GetDisposableTypes (line 60) | func (c *componentMeta) GetDisposableTypes() map[reflect.Type]uint16 {
    method GetFreeTypes (line 64) | func (c *componentMeta) GetFreeTypes() map[reflect.Type]uint16 {
    method Exist (line 68) | func (c *componentMeta) Exist(typ reflect.Type) bool {
    method GetOrCreateComponentMetaInfo (line 73) | func (c *componentMeta) GetOrCreateComponentMetaInfo(com IComponent) *...
    method GetComponentMetaInfoByIntType (line 81) | func (c *componentMeta) GetComponentMetaInfoByIntType(it uint16) *Comp...
    method GetComponentMetaInfoByType (line 89) | func (c *componentMeta) GetComponentMetaInfoByType(typ reflect.Type) *...
    method ComponentMetaInfoPrint (line 97) | func (c *componentMeta) ComponentMetaInfoPrint() {
  function NewComponentMeta (line 28) | func NewComponentMeta(world *ecsWorld) *componentMeta {

FILE: component_meta_test.go
  function BenchmarkIntTypeAndReflectType (line 8) | func BenchmarkIntTypeAndReflectType(b *testing.B) {

FILE: component_operate_task.go
  type opTask (line 7) | type opTask struct
    method Reset (line 14) | func (o *opTask) Reset() {
  type opTaskList (line 21) | type opTaskList struct
    method Clone (line 27) | func (o *opTaskList) Clone() *opTaskList {
    method Len (line 32) | func (o *opTaskList) Len() int {
    method Combine (line 36) | func (o *opTaskList) Combine(list *opTaskList) {
    method Append (line 47) | func (o *opTaskList) Append(task *opTask) {
    method Reset (line 58) | func (o *opTaskList) Reset() {
  type taskPool (line 66) | type taskPool struct
    method Get (line 80) | func (p *taskPool) Get() *opTask {
    method Put (line 88) | func (p *taskPool) Put(t *opTask) {
  function newTaskPool (line 70) | func newTaskPool() *taskPool {

FILE: component_set.go
  type IComponentSet (line 8) | type IComponentSet interface
  type ComponentSet (line 26) | type ComponentSet struct
  function NewComponentSet (line 32) | func NewComponentSet[T ComponentObject](meta *ComponentMetaInfo, initSiz...
  method Add (line 40) | func (c *ComponentSet[T]) Add(element *T, entity Entity) *T {
  method remove (line 50) | func (c *ComponentSet[T]) remove(entity Entity) *T {
  method Remove (line 55) | func (c *ComponentSet[T]) Remove(entity Entity) {
  method RemoveAndReturn (line 63) | func (c *ComponentSet[T]) RemoveAndReturn(entity Entity) *T {
  method getByEntity (line 68) | func (c *ComponentSet[T]) getByEntity(entity Entity) *T {
  method getPointerByEntity (line 72) | func (c *ComponentSet[T]) getPointerByEntity(entity Entity) unsafe.Point...
  method GetByEntity (line 76) | func (c *ComponentSet[T]) GetByEntity(entity Entity) any {
  method Get (line 80) | func (c *ComponentSet[T]) Get(entity Entity) *T {
  method pointer (line 84) | func (c *ComponentSet[T]) pointer() unsafe.Pointer {
  method changeCount (line 88) | func (c *ComponentSet[T]) changeCount() int64 {
  method changeReset (line 92) | func (c *ComponentSet[T]) changeReset() {
  method Sort (line 96) | func (c *ComponentSet[T]) Sort() {
  method GetComponent (line 121) | func (c *ComponentSet[T]) GetComponent(entity Entity) IComponent {
  method GetComponentRaw (line 125) | func (c *ComponentSet[T]) GetComponentRaw(entity Entity) unsafe.Pointer {
  method getPointerByIndex (line 129) | func (c *ComponentSet[T]) getPointerByIndex(index int64) unsafe.Pointer {
  method GetElementMeta (line 133) | func (c *ComponentSet[T]) GetElementMeta() *ComponentMetaInfo {
  method Range (line 137) | func (c *ComponentSet[T]) Range(fn func(com IComponent) bool) {
  function NewComponentSetIterator (line 143) | func NewComponentSetIterator[T ComponentObject](collection *ComponentSet...

FILE: component_set_test.go
  function TestComponentSet_Sort (line 8) | func TestComponentSet_Sort(t *testing.T) {
  function TestNewComponentSet (line 56) | func TestNewComponentSet(t *testing.T) {
  function BenchmarkComponentSet_Read (line 63) | func BenchmarkComponentSet_Read(b *testing.B) {

FILE: component_test.go
  function TestComponent_isValidComponentType (line 5) | func TestComponent_isValidComponentType(t *testing.T) {

FILE: component_utils.go
  function GetType (line 8) | func GetType[T ComponentObject]() reflect.Type {
  function ObjectToString (line 12) | func ObjectToString(in interface{}) string {

FILE: compound.go
  function NewCompound (line 5) | func NewCompound(initCap ...int) Compound {

FILE: compound_test.go
  function Test_getCompoundType (line 10) | func Test_getCompoundType(t *testing.T) {
  function TestCompound_find (line 49) | func TestCompound_find(t *testing.T) {
  function TestCompound_insertIndex (line 76) | func TestCompound_insertIndex(t *testing.T) {
  function TestCompound_Add (line 132) | func TestCompound_Add(t *testing.T) {
  function TestCompound_Remove (line 158) | func TestCompound_Remove(t *testing.T) {
  function BenchmarkCompound_Add (line 180) | func BenchmarkCompound_Add(b *testing.B) {
  constant CompoundSize (line 188) | CompoundSize = 20
  function BenchmarkCompound_Find (line 191) | func BenchmarkCompound_Find(b *testing.B) {
  function BenchmarkCompound_MapFind (line 202) | func BenchmarkCompound_MapFind(b *testing.B) {
  function BenchmarkCompound_BigMapFind (line 214) | func BenchmarkCompound_BigMapFind(b *testing.B) {

FILE: compound_utils.go
  function getCompoundType (line 7) | func getCompoundType(compound Compound) interface{} {

FILE: concurrent_map.go
  type Map (line 9) | type Map struct
  type readOnly (line 16) | type readOnly struct
  type entry (line 23) | type entry struct
  function newEntry (line 27) | func newEntry[V any](i V) *entry[V] {
  method Load (line 31) | func (m *Map[K, V]) Load(key K) (value V, ok bool) {
  method load (line 50) | func (e *entry[V]) load() (value V, ok bool) {
  method Store (line 58) | func (m *Map[K, V]) Store(key K, value V) {
  method tryStore (line 83) | func (e *entry[V]) tryStore(i *V) bool {
  method unexpungeLocked (line 95) | func (e *entry[V]) unexpungeLocked() (wasExpunged bool) {
  method storeLocked (line 99) | func (e *entry[V]) storeLocked(i *V) {
  method LoadOrStore (line 103) | func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
  method tryLoadOrStore (line 135) | func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) {
  method LoadAndDelete (line 159) | func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
  method Delete (line 179) | func (m *Map[K, V]) Delete(key K) {
  method delete (line 183) | func (e *entry[V]) delete() (value V, ok bool) {
  method Range (line 195) | func (m *Map[K, V]) Range(f func(key K, value V) bool) bool {
  method missLocked (line 221) | func (m *Map[K, V]) missLocked() {
  method dirtyLocked (line 231) | func (m *Map[K, V]) dirtyLocked() {
  method tryExpungeLocked (line 245) | func (e *entry[V]) tryExpungeLocked() (isExpunged bool) {

FILE: concurrent_map_test.go
  function TestConcurrentRange (line 15) | func TestConcurrentRange(t *testing.T) {

FILE: direct_api.go
  function RegisterSystem (line 8) | func RegisterSystem[T SystemObject, TP SystemPointer[T]](world IWorld, o...
  function AddFreeComponent (line 16) | func AddFreeComponent[T FreeComponentObject, TP FreeComponentPointer[T]]...
  function GetComponent (line 20) | func GetComponent[T ComponentObject](sys ISystem, entity Entity) *T {
  function GetComponentAll (line 24) | func GetComponentAll[T ComponentObject](sys ISystem) Iterator[T] {
  function GetRelated (line 44) | func GetRelated[T ComponentObject](sys ISystem, entity Entity) *T {
  function BindUtility (line 62) | func BindUtility[T UtilityObject, TP UtilityPointer[T]](si SystemInitCon...
  function GetUtility (line 74) | func GetUtility[T UtilityObject, TP UtilityPointer[T]](getter IUtilityGe...
  function TypeOf (line 86) | func TypeOf[T any]() reflect.Type {

FILE: entity.go
  type Entity (line 8) | type Entity
    method ToInt64 (line 10) | func (e Entity) ToInt64() int64 {
    method ToRealID (line 14) | func (e Entity) ToRealID() RealID {
  type RealID (line 18) | type RealID struct
    method ToInt64 (line 23) | func (r *RealID) ToInt64() int64 {
    method ToEntity (line 27) | func (r *RealID) ToEntity() Entity {
  type EntityIDGenerator (line 31) | type EntityIDGenerator struct
    method NewID (line 57) | func (e *EntityIDGenerator) NewID() Entity {
    method FreeID (line 74) | func (e *EntityIDGenerator) FreeID(entity Entity) {
    method delayFlush (line 91) | func (e *EntityIDGenerator) delayFlush() {
  function NewEntityIDGenerator (line 42) | func NewEntityIDGenerator(initSize int, delayCap int) *EntityIDGenerator {

FILE: entity_info.go
  type EntityInfo (line 3) | type EntityInfo struct
    method Destroy (line 8) | func (e *EntityInfo) Destroy(world IWorld) {
    method Entity (line 16) | func (e *EntityInfo) Entity() Entity {
    method Add (line 20) | func (e *EntityInfo) Add(world IWorld, components ...IComponent) {
    method Has (line 28) | func (e *EntityInfo) Has(its ...uint16) bool {
    method HasType (line 37) | func (e *EntityInfo) HasType(world *ecsWorld, components ...IComponent...
    method addToCompound (line 46) | func (e *EntityInfo) addToCompound(it uint16) {
    method removeFromCompound (line 50) | func (e *EntityInfo) removeFromCompound(it uint16) {
    method Remove (line 54) | func (e *EntityInfo) Remove(world IWorld, components ...IComponent) {

FILE: entity_set.go
  type EntitySet (line 3) | type EntitySet struct
    method Exist (line 13) | func (c *EntitySet) Exist(entity Entity) bool {
    method GetEntityInfo (line 18) | func (c *EntitySet) GetEntityInfo(entity Entity) (*EntityInfo, bool) {
    method Add (line 27) | func (c *EntitySet) Add(entityInfo EntityInfo) *EntityInfo {
    method Remove (line 32) | func (c *EntitySet) Remove(entity Entity) *EntityInfo {
  function NewEntityCollection (line 7) | func NewEntityCollection() *EntitySet {

FILE: entity_test.go
  function TestEntityIDGenerator_NewID (line 10) | func TestEntityIDGenerator_NewID(t *testing.T) {
  type _EntityTest (line 37) | type _EntityTest struct
    method NewID (line 42) | func (e *_EntityTest) NewID() Entity {
    method FreeID (line 56) | func (e *_EntityTest) FreeID(id Entity) {
  function BenchmarkEntityIDGenerator_New (line 61) | func BenchmarkEntityIDGenerator_New(b *testing.B) {
  function TestEntityIDGenerator_Free (line 87) | func TestEntityIDGenerator_Free(t *testing.T) {
  function BenchmarkEntityIDGenerator_NewFreeRandom (line 119) | func BenchmarkEntityIDGenerator_NewFreeRandom(b *testing.B) {

FILE: example/benchmark-0/components.go
  type Position (line 7) | type Position struct
  type Movement (line 14) | type Movement struct
  type HealthPoint (line 20) | type HealthPoint struct
  type Force (line 25) | type Force struct
  type Action (line 34) | type Action struct
  type Test1 (line 39) | type Test1 struct
  type Test2 (line 44) | type Test2 struct
  type Test3 (line 49) | type Test3 struct
  type Test4 (line 54) | type Test4 struct
  type Test5 (line 59) | type Test5 struct
  type Test6 (line 64) | type Test6 struct
  type Test7 (line 69) | type Test7 struct
  type Test8 (line 74) | type Test8 struct
  type Test9 (line 79) | type Test9 struct
  type Test10 (line 84) | type Test10 struct

FILE: example/benchmark-0/damage_system.go
  type Caster (line 9) | type Caster struct
  type Target (line 15) | type Target struct
  type DamageSystem (line 20) | type DamageSystem struct
    method Init (line 26) | func (d *DamageSystem) Init(si ecs.SystemInitConstraint) error {
    method Update (line 46) | func (d *DamageSystem) Update(event ecs.Event) {

FILE: example/benchmark-0/game_common.go
  constant PlayerCount (line 4) | PlayerCount = 200
  constant DummyMaxFor (line 5) | DummyMaxFor = 3000

FILE: example/benchmark-0/game_ecs.go
  type GameECS (line 8) | type GameECS struct
    method init (line 13) | func (g *GameECS) init(config *ecs.WorldConfig) {
    method attack (line 32) | func (g *GameECS) attack() {
  function DataGenerateECS (line 41) | func DataGenerateECS(game *GameECS) {

FILE: example/benchmark-0/game_normal.go
  type Player (line 9) | type Player struct
  type GameNormal (line 38) | type GameNormal struct
    method init (line 42) | func (g *GameNormal) init() {
    method doFrame (line 69) | func (g *GameNormal) doFrame(parallel bool, frame uint64, delta time.D...
    method SimuLoad1 (line 140) | func (g *GameNormal) SimuLoad1() {
    method SimuLoad2 (line 147) | func (g *GameNormal) SimuLoad2() {
    method SimuLoad3 (line 154) | func (g *GameNormal) SimuLoad3() {
    method SimuLoad4 (line 161) | func (g *GameNormal) SimuLoad4() {
    method SimuLoad5 (line 168) | func (g *GameNormal) SimuLoad5() {
    method SimuLoad6 (line 175) | func (g *GameNormal) SimuLoad6() {
    method SimuLoad7 (line 182) | func (g *GameNormal) SimuLoad7() {
    method SimuLoad8 (line 189) | func (g *GameNormal) SimuLoad8() {
    method SimuLoad9 (line 196) | func (g *GameNormal) SimuLoad9() {
    method SimuLoad10 (line 203) | func (g *GameNormal) SimuLoad10() {
    method SimuLoadParallel1 (line 211) | func (g *GameNormal) SimuLoadParallel1() {
    method SimuLoadParallel2 (line 218) | func (g *GameNormal) SimuLoadParallel2() {
    method SimuLoadParallel3 (line 225) | func (g *GameNormal) SimuLoadParallel3() {
    method SimuLoadParallel4 (line 232) | func (g *GameNormal) SimuLoadParallel4() {
    method SimuLoadParallel5 (line 239) | func (g *GameNormal) SimuLoadParallel5() {
    method SimuLoadParallel6 (line 246) | func (g *GameNormal) SimuLoadParallel6() {
    method SimuLoadParallel7 (line 253) | func (g *GameNormal) SimuLoadParallel7() {
    method SimuLoadParallel8 (line 260) | func (g *GameNormal) SimuLoadParallel8() {
    method SimuLoadParallel9 (line 267) | func (g *GameNormal) SimuLoadParallel9() {
    method SimuLoadParallel10 (line 274) | func (g *GameNormal) SimuLoadParallel10() {
    method DoMove (line 282) | func (g *GameNormal) DoMove(delta time.Duration) {
    method DoMoveParallel (line 290) | func (g *GameNormal) DoMoveParallel(delta time.Duration) {
    method DoDamage (line 298) | func (g *GameNormal) DoDamage() {
    method DoDamageParallel (line 325) | func (g *GameNormal) DoDamageParallel() {
  function DataGenerateNormal (line 46) | func DataGenerateNormal(normal *GameNormal) {

FILE: example/benchmark-0/main_benchmark_test.go
  function BenchmarkNormal (line 10) | func BenchmarkNormal(b *testing.B) {
  function BenchmarkNormalParallel (line 26) | func BenchmarkNormalParallel(b *testing.B) {
  function BenchmarkEcs (line 42) | func BenchmarkEcs(b *testing.B) {
  function BenchmarkEcsSingleCore (line 68) | func BenchmarkEcsSingleCore(b *testing.B) {

FILE: example/benchmark-0/main_test.go
  function TestFrame (line 14) | func TestFrame(t *testing.T) {
  function TestEcsOptimizer (line 32) | func TestEcsOptimizer(t *testing.T) {
  function TestOthers (line 57) | func TestOthers(t *testing.T) {

FILE: example/benchmark-0/move_system.go
  type MoveSystem (line 7) | type MoveSystem struct
    method Init (line 11) | func (m *MoveSystem) Init(si ecs.SystemInitConstraint) error {
    method Update (line 16) | func (m *MoveSystem) Update(event ecs.Event) {

FILE: example/benchmark-0/simu_load_system.go
  type Test1System (line 5) | type Test1System struct
    method Init (line 9) | func (t *Test1System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 14) | func (t *Test1System) Update(event ecs.Event) {
  type Test2System (line 23) | type Test2System struct
    method Init (line 27) | func (t *Test2System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 32) | func (t *Test2System) Update(event ecs.Event) {
  type Test3System (line 41) | type Test3System struct
    method Init (line 45) | func (t *Test3System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 50) | func (t *Test3System) Update(event ecs.Event) {
  type Test4System (line 59) | type Test4System struct
    method Init (line 63) | func (t *Test4System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 68) | func (t *Test4System) Update(event ecs.Event) {
  type Test5System (line 77) | type Test5System struct
    method Init (line 81) | func (t *Test5System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 86) | func (t *Test5System) Update(event ecs.Event) {
  type Test6System (line 95) | type Test6System struct
    method Init (line 99) | func (t *Test6System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 104) | func (t *Test6System) Update(event ecs.Event) {
  type Test7System (line 113) | type Test7System struct
    method Init (line 117) | func (t *Test7System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 122) | func (t *Test7System) Update(event ecs.Event) {
  type Test8System (line 131) | type Test8System struct
    method Init (line 135) | func (t *Test8System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 140) | func (t *Test8System) Update(event ecs.Event) {
  type Test9System (line 149) | type Test9System struct
    method Init (line 153) | func (t *Test9System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 158) | func (t *Test9System) Update(event ecs.Event) {
  type Test10System (line 167) | type Test10System struct
    method Init (line 171) | func (t *Test10System) Init(si ecs.SystemInitConstraint) error {
    method Update (line 176) | func (t *Test10System) Update(event ecs.Event) {

FILE: example/fake-simple-game-server/client/fake_client.go
  type FakeClient (line 12) | type FakeClient struct
    method Run (line 19) | func (f *FakeClient) Run(ctx context.Context) {
  function NewClient (line 15) | func NewClient() *FakeClient {

FILE: example/fake-simple-game-server/game/chat.go
  type ChatRoom (line 7) | type ChatRoom struct
    method Talk (line 17) | func (c *ChatRoom) Talk(content string) {
  function NewChatRoom (line 11) | func NewChatRoom(c *sync.Map) *ChatRoom {

FILE: example/fake-simple-game-server/game/empty_system.go
  type EmptySystem (line 5) | type EmptySystem struct
    method Init (line 12) | func (e *EmptySystem) Init(si ecs.SystemInitConstraint) error {
    method Start (line 17) | func (e *EmptySystem) Start(event ecs.Event) {
    method PreUpdate (line 21) | func (e *EmptySystem) PreUpdate(event ecs.Event) {
    method Update (line 29) | func (e *EmptySystem) Update(event ecs.Event) {
    method PostUpdate (line 37) | func (e *EmptySystem) PostUpdate(event ecs.Event) {

FILE: example/fake-simple-game-server/game/fake_game.go
  type Msg2Client (line 15) | type Msg2Client struct
  type FakeGame (line 20) | type FakeGame struct
    method Run (line 30) | func (f *FakeGame) Run(ctx context.Context) {
    method InitEcs (line 36) | func (f *FakeGame) InitEcs() {
    method EnterGame (line 50) | func (f *FakeGame) EnterGame(sess *Session) {
    method InitNetwork (line 70) | func (f *FakeGame) InitNetwork() {
    method OnClientEnter (line 115) | func (f *FakeGame) OnClientEnter(sess *Session) {
    method Dispatch (line 120) | func (f *FakeGame) Dispatch(pkg interface{}, sess *Session) {
    method Move (line 153) | func (f *FakeGame) Move(entity ecs.Entity, v int, dir [3]int) {
    method ChangeMovementTimeScale (line 166) | func (f *FakeGame) ChangeMovementTimeScale(timeScale float64) {
    method InitChat (line 179) | func (f *FakeGame) InitChat() {
  function NewGame (line 26) | func NewGame() *FakeGame {
  function SendToClient (line 108) | func SendToClient(sessionId int, content interface{}) {

FILE: example/fake-simple-game-server/game/move_component.go
  type Position (line 5) | type Position struct
  type Movement (line 12) | type Movement struct

FILE: example/fake-simple-game-server/game/move_system.go
  type MoveSystemUtility (line 9) | type MoveSystemUtility struct
    method UpdateTimeScale (line 13) | func (m *MoveSystemUtility) UpdateTimeScale(scale float64) error {
    method Move (line 23) | func (m *MoveSystemUtility) Move(entity ecs.Entity, v int, dir [3]int)...
  type MoveSystemData (line 34) | type MoveSystemData struct
  type MoveSystem (line 39) | type MoveSystem struct
    method Init (line 46) | func (m *MoveSystem) Init(si ecs.SystemInitConstraint) {
    method UpdateTimeScale (line 52) | func (m *MoveSystem) UpdateTimeScale(timeScale []interface{}) error {
    method Update (line 58) | func (m *MoveSystem) Update(event ecs.Event) {

FILE: example/fake-simple-game-server/game/player_component.go
  type PlayerComponent (line 5) | type PlayerComponent struct

FILE: example/fake-simple-game-server/game/position_sync_system.go
  type PlayerPosition (line 5) | type PlayerPosition struct
  type SyncSystem (line 10) | type SyncSystem struct
    method Init (line 14) | func (m *SyncSystem) Init(si ecs.SystemInitConstraint) error {
    method PostUpdate (line 19) | func (m *SyncSystem) PostUpdate(event ecs.Event) {

FILE: example/fake-simple-game-server/game/session.go
  type Session (line 8) | type Session struct

FILE: example/fake-simple-game-server/gm/gm.go
  type GM (line 10) | type GM struct
    method Run (line 18) | func (g *GM) Run(ctx context.Context, game *game.FakeGame) {
    method ChangeMovementTimeScale (line 34) | func (g *GM) ChangeMovementTimeScale(timeScale float64) {
  function NewGM (line 14) | func NewGM() *GM {

FILE: example/fake-simple-game-server/main.go
  function main (line 13) | func main() {

FILE: example/fake-simple-game-server/network/fake_net.go
  type TcpConn (line 3) | type TcpConn struct
    method Write (line 15) | func (t *TcpConn) Write(in interface{}) {
    method Read (line 20) | func (t *TcpConn) Read() interface{} {
  function NewTcpConn (line 8) | func NewTcpConn() *TcpConn {
  type FakeTcpServer (line 28) | type FakeTcpServer struct
    method Accept (line 42) | func (f *FakeTcpServer) Accept() *TcpConn {
  function Listen (line 30) | func Listen() (*FakeTcpServer, error) {
  function Dial (line 34) | func Dial(addr string) *TcpConn {

FILE: fixed_string.go
  constant __FixedMax (line 9) | __FixedMax = 128
  type FixedString (line 12) | type FixedString struct
  method Clear (line 17) | func (f *FixedString[T]) Clear() {
  method Empty (line 21) | func (f *FixedString[T]) Empty() bool {
  method Len (line 25) | func (f *FixedString[T]) Len() int {
  method String (line 29) | func (f *FixedString[T]) String() string {
  method Set (line 33) | func (f *FixedString[T]) Set(s string) {

FILE: fixed_string_test.go
  function TestFixedString_Generate (line 10) | func TestFixedString_Generate(t *testing.T) {
  function TestFixedString_String (line 30) | func TestFixedString_String(t *testing.T) {

FILE: fixed_string_utils.go
  type Fixed1 (line 3) | type Fixed1
  type Fixed2 (line 4) | type Fixed2
  type Fixed3 (line 5) | type Fixed3
  type Fixed4 (line 6) | type Fixed4
  type Fixed5 (line 7) | type Fixed5
  type Fixed6 (line 8) | type Fixed6
  type Fixed7 (line 9) | type Fixed7
  type Fixed8 (line 10) | type Fixed8
  type Fixed9 (line 11) | type Fixed9
  type Fixed10 (line 12) | type Fixed10
  type Fixed11 (line 13) | type Fixed11
  type Fixed12 (line 14) | type Fixed12
  type Fixed13 (line 15) | type Fixed13
  type Fixed14 (line 16) | type Fixed14
  type Fixed15 (line 17) | type Fixed15
  type Fixed16 (line 18) | type Fixed16
  type Fixed17 (line 19) | type Fixed17
  type Fixed18 (line 20) | type Fixed18
  type Fixed19 (line 21) | type Fixed19
  type Fixed20 (line 22) | type Fixed20
  type Fixed21 (line 23) | type Fixed21
  type Fixed22 (line 24) | type Fixed22
  type Fixed23 (line 25) | type Fixed23
  type Fixed24 (line 26) | type Fixed24
  type Fixed25 (line 27) | type Fixed25
  type Fixed26 (line 28) | type Fixed26
  type Fixed27 (line 29) | type Fixed27
  type Fixed28 (line 30) | type Fixed28
  type Fixed29 (line 31) | type Fixed29
  type Fixed30 (line 32) | type Fixed30
  type Fixed31 (line 33) | type Fixed31
  type Fixed32 (line 34) | type Fixed32
  type Fixed33 (line 35) | type Fixed33
  type Fixed34 (line 36) | type Fixed34
  type Fixed35 (line 37) | type Fixed35
  type Fixed36 (line 38) | type Fixed36
  type Fixed37 (line 39) | type Fixed37
  type Fixed38 (line 40) | type Fixed38
  type Fixed39 (line 41) | type Fixed39
  type Fixed40 (line 42) | type Fixed40
  type Fixed41 (line 43) | type Fixed41
  type Fixed42 (line 44) | type Fixed42
  type Fixed43 (line 45) | type Fixed43
  type Fixed44 (line 46) | type Fixed44
  type Fixed45 (line 47) | type Fixed45
  type Fixed46 (line 48) | type Fixed46
  type Fixed47 (line 49) | type Fixed47
  type Fixed48 (line 50) | type Fixed48
  type Fixed49 (line 51) | type Fixed49
  type Fixed50 (line 52) | type Fixed50
  type Fixed51 (line 53) | type Fixed51
  type Fixed52 (line 54) | type Fixed52
  type Fixed53 (line 55) | type Fixed53
  type Fixed54 (line 56) | type Fixed54
  type Fixed55 (line 57) | type Fixed55
  type Fixed56 (line 58) | type Fixed56
  type Fixed57 (line 59) | type Fixed57
  type Fixed58 (line 60) | type Fixed58
  type Fixed59 (line 61) | type Fixed59
  type Fixed60 (line 62) | type Fixed60
  type Fixed61 (line 63) | type Fixed61
  type Fixed62 (line 64) | type Fixed62
  type Fixed63 (line 65) | type Fixed63
  type Fixed64 (line 66) | type Fixed64
  type Fixed65 (line 67) | type Fixed65
  type Fixed66 (line 68) | type Fixed66
  type Fixed67 (line 69) | type Fixed67
  type Fixed68 (line 70) | type Fixed68
  type Fixed69 (line 71) | type Fixed69
  type Fixed70 (line 72) | type Fixed70
  type Fixed71 (line 73) | type Fixed71
  type Fixed72 (line 74) | type Fixed72
  type Fixed73 (line 75) | type Fixed73
  type Fixed74 (line 76) | type Fixed74
  type Fixed75 (line 77) | type Fixed75
  type Fixed76 (line 78) | type Fixed76
  type Fixed77 (line 79) | type Fixed77
  type Fixed78 (line 80) | type Fixed78
  type Fixed79 (line 81) | type Fixed79
  type Fixed80 (line 82) | type Fixed80
  type Fixed81 (line 83) | type Fixed81
  type Fixed82 (line 84) | type Fixed82
  type Fixed83 (line 85) | type Fixed83
  type Fixed84 (line 86) | type Fixed84
  type Fixed85 (line 87) | type Fixed85
  type Fixed86 (line 88) | type Fixed86
  type Fixed87 (line 89) | type Fixed87
  type Fixed88 (line 90) | type Fixed88
  type Fixed89 (line 91) | type Fixed89
  type Fixed90 (line 92) | type Fixed90
  type Fixed91 (line 93) | type Fixed91
  type Fixed92 (line 94) | type Fixed92
  type Fixed93 (line 95) | type Fixed93
  type Fixed94 (line 96) | type Fixed94
  type Fixed95 (line 97) | type Fixed95
  type Fixed96 (line 98) | type Fixed96
  type Fixed97 (line 99) | type Fixed97
  type Fixed98 (line 100) | type Fixed98
  type Fixed99 (line 101) | type Fixed99
  type Fixed100 (line 102) | type Fixed100
  type Fixed101 (line 103) | type Fixed101
  type Fixed102 (line 104) | type Fixed102
  type Fixed103 (line 105) | type Fixed103
  type Fixed104 (line 106) | type Fixed104
  type Fixed105 (line 107) | type Fixed105
  type Fixed106 (line 108) | type Fixed106
  type Fixed107 (line 109) | type Fixed107
  type Fixed108 (line 110) | type Fixed108
  type Fixed109 (line 111) | type Fixed109
  type Fixed110 (line 112) | type Fixed110
  type Fixed111 (line 113) | type Fixed111
  type Fixed112 (line 114) | type Fixed112
  type Fixed113 (line 115) | type Fixed113
  type Fixed114 (line 116) | type Fixed114
  type Fixed115 (line 117) | type Fixed115
  type Fixed116 (line 118) | type Fixed116
  type Fixed117 (line 119) | type Fixed117
  type Fixed118 (line 120) | type Fixed118
  type Fixed119 (line 121) | type Fixed119
  type Fixed120 (line 122) | type Fixed120
  type Fixed121 (line 123) | type Fixed121
  type Fixed122 (line 124) | type Fixed122
  type Fixed123 (line 125) | type Fixed123
  type Fixed124 (line 126) | type Fixed124
  type Fixed125 (line 127) | type Fixed125
  type Fixed126 (line 128) | type Fixed126
  type Fixed127 (line 129) | type Fixed127
  type Fixed128 (line 130) | type Fixed128

FILE: goroutine_id.go
  function goroutineID (line 20) | func goroutineID() int64 {
  function getg (line 24) | func getg() unsafe.Pointer {
  function add (line 30) | func add(p unsafe.Pointer, x uintptr) unsafe.Pointer
  function getm (line 33) | func getm() unsafe.Pointer
  function offset (line 41) | func offset(typ, field string) uintptr {
  function typesByString (line 48) | func typesByString(s string) []unsafe.Pointer
  function toType (line 51) | func toType(t unsafe.Pointer) reflect.Type

FILE: goroutine_pool.go
  type Worker (line 8) | type Worker struct
    method Start (line 15) | func (w *Worker) Start() {
  type Pool (line 40) | type Pool struct
    method Add (line 78) | func (p *Pool) Add(job func(), hashKey ...uint32) {
    method Start (line 87) | func (p *Pool) Start() {
    method Size (line 96) | func (p *Pool) Size() uint32 {
    method Release (line 101) | func (p *Pool) Release() {
  function NewPool (line 48) | func NewPool(size uint32, jobQueueSize uint32) *Pool {

FILE: goroutine_pool_benchmark_test.go
  constant runTimes (line 9) | runTimes  = 25
  constant poolSize (line 10) | poolSize  = 12
  constant queueSize (line 11) | queueSize = 50
  function demoTask (line 14) | func demoTask() {
  function BenchmarkGoroutine (line 19) | func BenchmarkGoroutine(b *testing.B) {
  function BenchmarkGpool (line 36) | func BenchmarkGpool(b *testing.B) {

FILE: internal_type_mock.go
  type Signed (line 10) | type Signed interface
  type Unsigned (line 17) | type Unsigned interface
  type Integer (line 24) | type Integer interface
  type Float (line 31) | type Float interface
  type Complex (line 38) | type Complex interface
  type Ordered (line 46) | type Ordered interface
  type eface (line 50) | type eface struct
  type iface (line 55) | type iface struct
  type itab (line 60) | type itab struct
  type interfacetype (line 68) | type interfacetype struct
  type _type (line 74) | type _type struct
  type name (line 88) | type name struct
  type imethod (line 92) | type imethod struct
  type tflag (line 97) | type tflag
  type nameOff (line 98) | type nameOff
  type typeOff (line 99) | type typeOff

FILE: logger.go
  type Logger (line 10) | type Logger interface
  type StdLogLevel (line 24) | type StdLogLevel
  constant StdLogLevelDebug (line 27) | StdLogLevelDebug StdLogLevel = iota
  constant StdLogLevelInfo (line 28) | StdLogLevelInfo
  constant StdLogLevelError (line 29) | StdLogLevelError
  constant StdLogLevelFatal (line 30) | StdLogLevelFatal
  constant StdLogLevelNoPrint (line 31) | StdLogLevelNoPrint
  type StdLog (line 34) | type StdLog struct
    method Debug (line 50) | func (p StdLog) Debug(v ...interface{}) {
    method Debugf (line 54) | func (p StdLog) Debugf(format string, v ...interface{}) {
    method Info (line 58) | func (p StdLog) Info(v ...interface{}) {
    method Infof (line 65) | func (p StdLog) Infof(format string, v ...interface{}) {
    method Error (line 72) | func (p StdLog) Error(v ...interface{}) {
    method Errorf (line 89) | func (p StdLog) Errorf(format string, v ...interface{}) {
    method Fatal (line 106) | func (p StdLog) Fatal(v ...interface{}) {
    method Fatalf (line 114) | func (p StdLog) Fatalf(format string, v ...interface{}) {
  function NewStdLog (line 39) | func NewStdLog(level ...StdLogLevel) *StdLog {

FILE: metrics.go
  type Metrics (line 5) | type Metrics struct
    method NewReporter (line 11) | func (m *Metrics) NewReporter(name string) *MetricReporter {
    method Print (line 20) | func (m *Metrics) Print() {
  function NewMetrics (line 29) | func NewMetrics(enable bool, print bool) *Metrics {
  type reporterStep (line 37) | type reporterStep struct
  type MetricReporter (line 42) | type MetricReporter struct
    method Start (line 51) | func (m *MetricReporter) Start() {
    method Sample (line 59) | func (m *MetricReporter) Sample(name string) {
    method Stop (line 72) | func (m *MetricReporter) Stop() {
    method Print (line 83) | func (m *MetricReporter) Print(force ...bool) {

FILE: optimizer.go
  type ShapeInfo (line 9) | type ShapeInfo struct
  type OptimizerReporter (line 15) | type OptimizerReporter struct
    method init (line 19) | func (o *OptimizerReporter) init() {
  type optimizer (line 23) | type optimizer struct
    method collect (line 37) | func (o *optimizer) collect() {
    method optimize (line 77) | func (o *optimizer) optimize(IdleTime time.Duration, force bool) {
    method expire (line 94) | func (o *optimizer) expire() time.Duration {
    method elapsed (line 98) | func (o *optimizer) elapsed() time.Duration {
    method elapsedStep (line 102) | func (o *optimizer) elapsedStep() time.Duration {
    method memTidy (line 109) | func (o *optimizer) memTidy(force bool) {
  function newOptimizer (line 32) | func newOptimizer(world *ecsWorld) *optimizer {

FILE: optimizer_benchmark_test.go
  constant testOptimizerDummyMaxFor (line 10) | testOptimizerDummyMaxFor = 10
  constant testOptimizerEntityMax (line 11) | testOptimizerEntityMax   = 1000000
  type __optimizer_Bench_C_1 (line 14) | type __optimizer_Bench_C_1 struct
  type __optimizer_Bench_C_2 (line 19) | type __optimizer_Bench_C_2 struct
  type __optimizer_Bench_S_1 (line 24) | type __optimizer_Bench_S_1 struct
    method Init (line 28) | func (t *__optimizer_Bench_S_1) Init(si SystemInitConstraint) {
    method Update (line 32) | func (t *__optimizer_Bench_S_1) Update(event Event) {
  type __optimizer_Bench_GameECS (line 49) | type __optimizer_Bench_GameECS struct
    method init (line 54) | func (g *__optimizer_Bench_GameECS) init() {
  function BenchmarkNoOptimizer (line 76) | func BenchmarkNoOptimizer(b *testing.B) {
  function BenchmarkWithOptimizer (line 93) | func BenchmarkWithOptimizer(b *testing.B) {
  function BenchmarkTest (line 112) | func BenchmarkTest(b *testing.B) {
  function BenchmarkTest2 (line 124) | func BenchmarkTest2(b *testing.B) {

FILE: ordered_int_set.go
  type OrderedIntSet (line 3) | type OrderedIntSet
  method InsertIndex (line 5) | func (c *OrderedIntSet[T]) InsertIndex(it T) int {
  method Find (line 31) | func (c *OrderedIntSet[T]) Find(it T) int {
  method Exist (line 48) | func (c *OrderedIntSet[T]) Exist(it T) bool {
  method IsSubSet (line 52) | func (c *OrderedIntSet[T]) IsSubSet(subSet OrderedIntSet[T]) bool {
  method Add (line 74) | func (c *OrderedIntSet[T]) Add(it T) bool {
  method Remove (line 85) | func (c *OrderedIntSet[T]) Remove(it T) bool {

FILE: ordered_int_set_test.go
  function TestOrderedIntSet_Add (line 5) | func TestOrderedIntSet_Add(t *testing.T) {
  function TestOrderedIntSet_Remove (line 20) | func TestOrderedIntSet_Remove(t *testing.T) {
  function TestOrderedIntSet_InsertIndex (line 39) | func TestOrderedIntSet_InsertIndex(t *testing.T) {
  function TestOrderedIntSet_Find (line 59) | func TestOrderedIntSet_Find(t *testing.T) {
  function TestOrderedIntSet_IsSubSet (line 79) | func TestOrderedIntSet_IsSubSet(t *testing.T) {

FILE: serialize.go
  type ICustomSerialize (line 4) | type ICustomSerialize interface

FILE: shape_getter.go
  type IShape (line 8) | type IShape interface
  type shapeBase (line 13) | type shapeBase struct
    method base (line 19) | func (s *shapeBase) base() *shapeBase {
    method init (line 23) | func (s *shapeBase) init(typ reflect.Type, getter IShape) {
  type ShapeIndices (line 30) | type ShapeIndices struct
  type Shape (line 37) | type Shape struct
  function NewShape (line 49) | func NewShape[T any](initializer SystemInitConstraint) *Shape[T] {
  method IsValid (line 97) | func (s *Shape[T]) IsValid() bool {
  method getType (line 101) | func (s *Shape[T]) getType() reflect.Type {
  method Get (line 108) | func (s *Shape[T]) Get() IShapeIterator[T] {
  method GetSpecific (line 144) | func (s *Shape[T]) GetSpecific(entity Entity) (*T, bool) {
  method SetGuide (line 162) | func (s *Shape[T]) SetGuide(component IComponent) *Shape[T] {

FILE: shape_getter_test.go
  type __ShapeGetter_Test_C_1 (line 8) | type __ShapeGetter_Test_C_1 struct
  type __ShapeGetter_Test_C_2 (line 13) | type __ShapeGetter_Test_C_2 struct
  type __ShapeGetter_Test_Shape_1 (line 18) | type __ShapeGetter_Test_Shape_1 struct
  type __ShapeGetter_Test_S_1 (line 23) | type __ShapeGetter_Test_S_1 struct
    method Init (line 29) | func (t *__ShapeGetter_Test_S_1) Init(initializer SystemInitConstraint) {
    method Update (line 38) | func (t *__ShapeGetter_Test_S_1) Update(event Event) {
  function TestNewShapeGetter (line 46) | func TestNewShapeGetter(t *testing.T) {

FILE: shape_iter.go
  type IShapeIterator (line 5) | type IShapeIterator interface
  type ShapeIter (line 12) | type ShapeIter struct
  function EmptyShapeIter (line 21) | func EmptyShapeIter[T any]() IShapeIterator[T] {
  function NewShapeIterator (line 25) | func NewShapeIterator[T any](indices ShapeIndices, mainKeyIndex int) ISh...
  method tryNext (line 37) | func (s *ShapeIter[T]) tryNext() *T {
  method getSiblings (line 66) | func (s *ShapeIter[T]) getSiblings(entity Entity) bool {
  method trans (line 80) | func (s *ShapeIter[T]) trans(i int, subPointer unsafe.Pointer) {
  method End (line 88) | func (s *ShapeIter[T]) End() bool {
  method Begin (line 95) | func (s *ShapeIter[T]) Begin() *T {
  method Val (line 104) | func (s *ShapeIter[T]) Val() *T {
  method Next (line 111) | func (s *ShapeIter[T]) Next() *T {

FILE: sparse_array.go
  type SparseArray (line 3) | type SparseArray struct
  function NewSparseArray (line 12) | func NewSparseArray[K Integer, V any](initSize ...int) *SparseArray[K, V] {
  method Add (line 44) | func (g *SparseArray[K, V]) Add(key K, value *V) *V {
  method Remove (line 77) | func (g *SparseArray[K, V]) Remove(key K) *V {
  method Exist (line 95) | func (g *SparseArray[K, V]) Exist(key K) bool {
  method Get (line 102) | func (g *SparseArray[K, V]) Get(key K) *V {
  method Clear (line 113) | func (g *SparseArray[K, V]) Clear() {
  method shrink (line 129) | func (g *SparseArray[K, V]) shrink(key K) {

FILE: system.go
  type SystemState (line 9) | type SystemState
  constant SystemStateInvalid (line 12) | SystemStateInvalid SystemState = iota
  constant SystemStateInit (line 13) | SystemStateInit
  constant SystemStateStart (line 14) | SystemStateStart
  constant SystemStatePause (line 15) | SystemStatePause
  constant SystemStateUpdate (line 16) | SystemStateUpdate
  constant SystemStateDestroy (line 17) | SystemStateDestroy
  constant SystemStateDestroyed (line 18) | SystemStateDestroyed
  type SystemInitConstraint (line 21) | type SystemInitConstraint struct
    method getSystem (line 25) | func (s *SystemInitConstraint) getSystem() ISystem {
    method SetBroken (line 32) | func (s *SystemInitConstraint) SetBroken(reason string) {
    method isValid (line 37) | func (s *SystemInitConstraint) isValid() bool {
  type ISystem (line 41) | type ISystem interface
  type SystemObject (line 71) | type SystemObject interface
  type SystemPointer (line 75) | type SystemPointer interface
  type systemIdentification (line 80) | type systemIdentification struct
    method __SystemIdentification (line 82) | func (s systemIdentification) __SystemIdentification() {}
  type System (line 84) | type System struct
  method instance (line 101) | func (s *System[T]) instance() (sys ISystem) {
  method rawInstance (line 106) | func (s *System[T]) rawInstance() *T {
  method ID (line 110) | func (s *System[T]) ID() int64 {
  method SetRequirements (line 117) | func (s *System[T]) SetRequirements(initializer SystemInitConstraint, rq...
  method setRequirementsInternal (line 124) | func (s *System[T]) setRequirementsInternal(rqs ...IRequirement) {
  method isInitialized (line 135) | func (s *System[T]) isInitialized() bool {
  method setRequirements (line 139) | func (s *System[T]) setRequirements(initializer SystemInitConstraint, rq...
  method setUtility (line 152) | func (s *System[T]) setUtility(u IUtility) {
  method setSecurity (line 156) | func (s *System[T]) setSecurity(isSafe bool) {
  method isThreadSafe (line 159) | func (s *System[T]) isThreadSafe() bool {
  method GetUtility (line 163) | func (s *System[T]) GetUtility() IUtility {
  method pause (line 167) | func (s *System[T]) pause() {
  method resume (line 173) | func (s *System[T]) resume() {
  method stop (line 179) | func (s *System[T]) stop() {
  method getState (line 185) | func (s *System[T]) getState() SystemState {
  method setState (line 189) | func (s *System[T]) setState(state SystemState) {
  method setBroken (line 193) | func (s *System[T]) setBroken() {
  method isValid (line 197) | func (s *System[T]) isValid() bool {
  method setExecuting (line 201) | func (s *System[T]) setExecuting(isExecuting bool) {
  method isExecuting (line 205) | func (s *System[T]) isExecuting() bool {
  method GetRequirements (line 209) | func (s *System[T]) GetRequirements() map[reflect.Type]IRequirement {
  method IsRequire (line 213) | func (s *System[T]) IsRequire(com IComponent) bool {
  method isRequire (line 217) | func (s *System[T]) isRequire(typ reflect.Type) bool {
  method baseInit (line 222) | func (s *System[T]) baseInit(world *ecsWorld, ins ISystem) {
  method getPointer (line 250) | func (s *System[T]) getPointer() unsafe.Pointer {
  method Type (line 254) | func (s *System[T]) Type() reflect.Type {
  method setOrder (line 261) | func (s *System[T]) setOrder(order Order) {
  method Order (line 269) | func (s *System[T]) Order() Order {
  method World (line 273) | func (s *System[T]) World() IWorld {
  method GetEntityInfo (line 277) | func (s *System[T]) GetEntityInfo(entity Entity) (*EntityInfo, bool) {
  method getOptimizer (line 282) | func (s *System[T]) getOptimizer() *OptimizerReporter {
  method getGetterCache (line 290) | func (s *System[T]) getGetterCache() *GetterCache {

FILE: system_event.go
  type Event (line 5) | type Event struct
  type InitReceiver (line 10) | type InitReceiver interface
  type SyncBeforeStartReceiver (line 14) | type SyncBeforeStartReceiver interface
  type StartReceiver (line 18) | type StartReceiver interface
  type SyncAfterStartReceiver (line 22) | type SyncAfterStartReceiver interface
  type SyncBeforePreUpdateReceiver (line 26) | type SyncBeforePreUpdateReceiver interface
  type PreUpdateReceiver (line 30) | type PreUpdateReceiver interface
  type SyncAfterPreUpdateReceiver (line 34) | type SyncAfterPreUpdateReceiver interface
  type SyncBeforeUpdateReceiver (line 38) | type SyncBeforeUpdateReceiver interface
  type UpdateReceiver (line 42) | type UpdateReceiver interface
  type SyncAfterUpdateReceiver (line 46) | type SyncAfterUpdateReceiver interface
  type SyncBeforePostUpdateReceiver (line 50) | type SyncBeforePostUpdateReceiver interface
  type PostUpdateReceiver (line 54) | type PostUpdateReceiver interface
  type SyncAfterPostUpdateReceiver (line 58) | type SyncAfterPostUpdateReceiver interface
  type SyncBeforeDestroyReceiver (line 62) | type SyncBeforeDestroyReceiver interface
  type DestroyReceiver (line 66) | type DestroyReceiver interface
  type SyncAfterPostDestroyReceiver (line 70) | type SyncAfterPostDestroyReceiver interface

FILE: system_flow.go
  constant StageSyncBeforeStart (line 10) | StageSyncBeforeStart Stage = iota
  constant StageStart (line 11) | StageStart
  constant StageSyncAfterStart (line 12) | StageSyncAfterStart
  constant StageSyncBeforePreUpdate (line 14) | StageSyncBeforePreUpdate
  constant StagePreUpdate (line 15) | StagePreUpdate
  constant StageSyncAfterPreUpdate (line 16) | StageSyncAfterPreUpdate
  constant StageSyncBeforeUpdate (line 18) | StageSyncBeforeUpdate
  constant StageUpdate (line 19) | StageUpdate
  constant StageSyncAfterUpdate (line 20) | StageSyncAfterUpdate
  constant StageSyncBeforePostUpdate (line 22) | StageSyncBeforePostUpdate
  constant StagePostUpdate (line 23) | StagePostUpdate
  constant StageSyncAfterPostUpdate (line 24) | StageSyncAfterPostUpdate
  constant StageSyncBeforeDestroy (line 26) | StageSyncBeforeDestroy
  constant StageDestroy (line 27) | StageDestroy
  constant StageSyncAfterDestroy (line 28) | StageSyncAfterDestroy
  type Stage (line 32) | type Stage
  type Order (line 35) | type Order
  constant OrderFront (line 38) | OrderFront   Order = -1
  constant OrderInvalid (line 39) | OrderInvalid Order = 0
  constant OrderAppend (line 40) | OrderAppend  Order = 99999999
  constant OrderDefault (line 41) | OrderDefault Order = OrderAppend
  type SystemGroupList (line 45) | type SystemGroupList
  type systemFlow (line 48) | type systemFlow struct
    method init (line 67) | func (p *systemFlow) init() {
    method reset (line 92) | func (p *systemFlow) reset() {
    method flushTempTask (line 104) | func (p *systemFlow) flushTempTask() {
    method systemUpdate (line 118) | func (p *systemFlow) systemUpdate(event Event) {
    method run (line 280) | func (p *systemFlow) run(event Event) {
    method register (line 304) | func (p *systemFlow) register(system ISystem) {
    method isImpEvent (line 349) | func (p *systemFlow) isImpEvent(system ISystem, period Stage) bool {
    method stop (line 386) | func (p *systemFlow) stop() {
    method SystemInfoPrint (line 390) | func (p *systemFlow) SystemInfoPrint() {
  function newSystemFlow (line 56) | func newSystemFlow(runtime *ecsWorld) *systemFlow {

FILE: system_group.go
  type Node (line 11) | type Node struct
    method isFriend (line 17) | func (p *Node) isFriend(node *Node) bool {
    method attach (line 31) | func (p *Node) attach(node *Node) {
  type SystemGroup (line 50) | type SystemGroup struct
    method refCount (line 74) | func (p *SystemGroup) refCount(rqs map[reflect.Type]IRequirement) int {
    method resort (line 82) | func (p *SystemGroup) resort() {
    method resetIter (line 119) | func (p *SystemGroup) resetIter() {
    method systemCount (line 141) | func (p *SystemGroup) systemCount() int {
    method batchCount (line 145) | func (p *SystemGroup) batchCount() int {
    method maxSystemCountPeerBatch (line 149) | func (p *SystemGroup) maxSystemCountPeerBatch() int {
    method all (line 154) | func (p *SystemGroup) all() []ISystem {
    method insert (line 163) | func (p *SystemGroup) insert(sys ISystem) {
    method has (line 188) | func (p *SystemGroup) has(sys ISystem) bool {
    method remove (line 198) | func (p *SystemGroup) remove(sys ISystem) {
    method iter (line 224) | func (p *SystemGroup) iter() *SystemGroupIterator {
  function NewSystemGroup (line 61) | func NewSystemGroup() *SystemGroup {
  type SystemGroupIterator (line 241) | type SystemGroupIterator struct
    method Begin (line 250) | func (s *SystemGroupIterator) Begin() []ISystem {
    method Next (line 262) | func (s *SystemGroupIterator) Next() []ISystem {
    method End (line 286) | func (s *SystemGroupIterator) End() bool {

FILE: system_group_test.go
  type __systemGroup_Test_C_1 (line 7) | type __systemGroup_Test_C_1 struct
  type __systemGroup_Test_C_2 (line 10) | type __systemGroup_Test_C_2 struct
  type __systemGroup_Test_C_3 (line 13) | type __systemGroup_Test_C_3 struct
  type __systemGroup_Test_C_4 (line 16) | type __systemGroup_Test_C_4 struct
  type __systemGroup_Test_C_5 (line 19) | type __systemGroup_Test_C_5 struct
  type __systemGroup_Test_C_6 (line 22) | type __systemGroup_Test_C_6 struct
  type __systemGroup_Test_C_7 (line 25) | type __systemGroup_Test_C_7 struct
  type __systemGroup_Test_C_8 (line 28) | type __systemGroup_Test_C_8 struct
  type __systemGroup_Test_C_9 (line 31) | type __systemGroup_Test_C_9 struct
  type __systemGroup_Test_C_10 (line 34) | type __systemGroup_Test_C_10 struct
  type __systemGroup_Test_S_1 (line 38) | type __systemGroup_Test_S_1 struct
    method Call (line 49) | func (p *__systemGroup_Test_S_1) Call(label int) interface{} {
  function NewTestSystem (line 43) | func NewTestSystem(ID int, rqs ...IRequirement) *__systemGroup_Test_S_1 {
  function newSystemGroupTestSystem (line 57) | func newSystemGroupTestSystem() []ISystem {
  function TestNewSystemGroupIterEmpty (line 73) | func TestNewSystemGroupIterEmpty(t *testing.T) {
  function TestNewSystemGroupIter (line 87) | func TestNewSystemGroupIter(t *testing.T) {
  function TestNewSystemGroupIterTemp (line 106) | func TestNewSystemGroupIterTemp(t *testing.T) {
  function BenchmarkSystemGroupIter (line 132) | func BenchmarkSystemGroupIter(b *testing.B) {

FILE: system_requirement.go
  constant ComponentReadWrite (line 9) | ComponentReadWrite ComponentPermission = 0
  constant ComponentReadOnly (line 10) | ComponentReadOnly  ComponentPermission = 1
  type ComponentPermission (line 13) | type ComponentPermission
  type IRequirement (line 15) | type IRequirement interface
  type ReadOnly (line 21) | type ReadOnly struct
  method Type (line 23) | func (r *ReadOnly[T]) Type() reflect.Type {
  method getPermission (line 27) | func (r *ReadOnly[T]) getPermission() ComponentPermission {
  method check (line 31) | func (r *ReadOnly[T]) check(initializer SystemInitConstraint) {

FILE: unordered_collection.go
  constant InitMaxSize (line 8) | InitMaxSize = 1024 * 16
  constant SeqMax (line 10) | SeqMax uint32 = 0xFFFFFFFF
  type UnorderedCollection (line 13) | type UnorderedCollection struct
  function NewUnorderedCollection (line 20) | func NewUnorderedCollection[T any](initSize ...int) *UnorderedCollection...
  method Get (line 34) | func (c *UnorderedCollection[T]) Get(idx int64) *T {
  method Add (line 38) | func (c *UnorderedCollection[T]) Add(element *T) (*T, int64) {
  method Remove (line 49) | func (c *UnorderedCollection[T]) Remove(idx int64) (*T, int64, int64) {
  method Len (line 62) | func (c *UnorderedCollection[T]) Len() int {
  method Range (line 66) | func (c *UnorderedCollection[T]) Range(f func(element *T) bool) {
  method Clear (line 74) | func (c *UnorderedCollection[T]) Clear() {
  method shrink (line 79) | func (c *UnorderedCollection[T]) shrink() {
  method getIndexByElePointer (line 94) | func (c *UnorderedCollection[T]) getIndexByElePointer(element *T) int64 {
  function NewUnorderedCollectionIterator (line 109) | func NewUnorderedCollectionIterator[T ComponentObject](collection *Unord...

FILE: unordered_collection_test.go
  type __unorderedCollection_Test_item (line 8) | type __unorderedCollection_Test_item struct
  function TestUnorderedCollection_Iterator (line 14) | func TestUnorderedCollection_Iterator(t *testing.T) {
  function BenchmarkUnorderedCollection_SliceIter (line 48) | func BenchmarkUnorderedCollection_SliceIter(b *testing.B) {
  function BenchmarkUnorderedCollection_Write (line 73) | func BenchmarkUnorderedCollection_Write(b *testing.B) {
  function BenchmarkUnorderedCollection_SliceRead (line 86) | func BenchmarkUnorderedCollection_SliceRead(b *testing.B) {
  function BenchmarkUnorderedCollection_Read (line 126) | func BenchmarkUnorderedCollection_Read(b *testing.B) {
  function BenchmarkUnorderedCollection_ReadUnsafe (line 145) | func BenchmarkUnorderedCollection_ReadUnsafe(b *testing.B) {

FILE: unordered_collection_with_id.go
  type ICollectionWithID (line 5) | type ICollectionWithID interface
  type UnorderedCollectionWithID (line 13) | type UnorderedCollectionWithID struct
  function NewUnorderedCollectionWithID (line 20) | func NewUnorderedCollectionWithID[T any](initSize ...int) *UnorderedColl...
  method getID (line 38) | func (c *UnorderedCollectionWithID[T]) getID() int64 {
  method Add (line 49) | func (c *UnorderedCollectionWithID[T]) Add(element *T, elementID ...int6...
  method remove (line 63) | func (c *UnorderedCollectionWithID[T]) remove(id int64) *T {
  method Remove (line 80) | func (c *UnorderedCollectionWithID[T]) Remove(id int64) {
  method RemoveAndReturn (line 84) | func (c *UnorderedCollectionWithID[T]) RemoveAndReturn(id int64) *T {
  method getByID (line 89) | func (c *UnorderedCollectionWithID[T]) getByID(id int64) *T {
  method GetByID (line 97) | func (c *UnorderedCollectionWithID[T]) GetByID(id int64) any {
  method GetByIndex (line 101) | func (c *UnorderedCollectionWithID[T]) GetByIndex(idx int64) any {
  function NewUnorderedCollectionWithIDIterator (line 105) | func NewUnorderedCollectionWithIDIterator[T ComponentObject](collection ...

FILE: unordered_collection_with_id_test.go
  function BenchmarkUnorderedCollectionWithID_Read (line 7) | func BenchmarkUnorderedCollectionWithID_Read(b *testing.B) {
  function BenchmarkUnorderedCollectionWithID_Iter (line 26) | func BenchmarkUnorderedCollectionWithID_Iter(b *testing.B) {
  function BenchmarkUnorderedCollectionWithID_SliceWrite (line 49) | func BenchmarkUnorderedCollectionWithID_SliceWrite(b *testing.B) {
  function BenchmarkUnorderedCollectionWithID_SliceRead (line 62) | func BenchmarkUnorderedCollectionWithID_SliceRead(b *testing.B) {

FILE: utility.go
  type IUtilityGetter (line 8) | type IUtilityGetter interface
  type UtilityGetter (line 12) | type UtilityGetter struct
    method getWorld (line 16) | func (g UtilityGetter) getWorld() IWorld {
  type UtilityObject (line 20) | type UtilityObject interface
  type UtilityPointer (line 24) | type UtilityPointer interface
  type IUtility (line 29) | type IUtility interface
  type utilityIdentification (line 36) | type utilityIdentification struct
    method __UtilityIdentification (line 38) | func (u utilityIdentification) __UtilityIdentification() {}
  type Utility (line 40) | type Utility struct
  method setWorld (line 46) | func (u *Utility[T]) setWorld(w IWorld) {
  method setSystem (line 50) | func (u *Utility[T]) setSystem(sys ISystem) {
  method GetSystem (line 54) | func (u *Utility[T]) GetSystem() ISystem {
  method getPointer (line 58) | func (u *Utility[T]) getPointer() unsafe.Pointer {
  method Type (line 62) | func (u *Utility[T]) Type() reflect.Type {
  method GetRequirements (line 66) | func (u *Utility[T]) GetRequirements() map[reflect.Type]IRequirement {
  method SystemPause (line 70) | func (u *Utility[T]) SystemPause() {
  method SystemResume (line 74) | func (u *Utility[T]) SystemResume() {
  method SystemStop (line 78) | func (u *Utility[T]) SystemStop() {

FILE: utils.go
  function init (line 20) | func init() {
  function LocalUniqueID (line 24) | func LocalUniqueID() int64 {
  function Try (line 42) | func Try(task func(), catch ...func(error)) {
  function TryAndReport (line 64) | func TryAndReport(task func() error) (err error) {
  function IsPureValueType (line 84) | func IsPureValueType(typ reflect.Type) bool {
  function StrHash (line 105) | func StrHash(str string, groupCount int) int {
  function memcmp (line 113) | func memcmp(a unsafe.Pointer, b unsafe.Pointer, len uintptr) (ret bool) {

FILE: utils_test.go
  function TestUniqueID (line 7) | func TestUniqueID(t *testing.T) {
  function BenchmarkUniqueID (line 21) | func BenchmarkUniqueID(b *testing.B) {

FILE: world.go
  type WorldStatus (line 11) | type WorldStatus
  constant WorldStatusInitializing (line 14) | WorldStatusInitializing WorldStatus = iota
  constant WorldStatusInitialized (line 15) | WorldStatusInitialized
  constant WorldStatusRunning (line 16) | WorldStatusRunning
  constant WorldStatusStop (line 17) | WorldStatusStop
  type WorldConfig (line 20) | type WorldConfig struct
  function NewDefaultWorldConfig (line 35) | func NewDefaultWorldConfig() *WorldConfig {
  type IWorld (line 50) | type IWorld interface
  type ecsWorld (line 79) | type ecsWorld struct
    method init (line 99) | func (w *ecsWorld) init(config *WorldConfig) *ecsWorld {
    method base (line 142) | func (w *ecsWorld) base() *ecsWorld {
    method getID (line 146) | func (w *ecsWorld) getID() int64 {
    method switchMainThread (line 150) | func (w *ecsWorld) switchMainThread() {
    method startup (line 154) | func (w *ecsWorld) startup() {
    method update (line 169) | func (w *ecsWorld) update() {
    method optimize (line 187) | func (w *ecsWorld) optimize(t time.Duration, force bool) {
    method stop (line 191) | func (w *ecsWorld) stop() {
    method setStatus (line 195) | func (w *ecsWorld) setStatus(status WorldStatus) {
    method getUtilityGetter (line 199) | func (w *ecsWorld) getUtilityGetter() UtilityGetter {
    method addUtility (line 206) | func (w *ecsWorld) addUtility(utility IUtility) {
    method getUtilityForT (line 209) | func (w *ecsWorld) getUtilityForT(typ reflect.Type) (unsafe.Pointer, b...
    method getStatus (line 214) | func (w *ecsWorld) getStatus() WorldStatus {
    method getMetrics (line 218) | func (w *ecsWorld) getMetrics() *Metrics {
    method registerSystem (line 222) | func (w *ecsWorld) registerSystem(system ISystem) {
    method registerComponent (line 227) | func (w *ecsWorld) registerComponent(component IComponent) {
    method getSystem (line 232) | func (w *ecsWorld) getSystem(sys reflect.Type) (ISystem, bool) {
    method addJob (line 240) | func (w *ecsWorld) addJob(job func(), hashKey ...uint32) {
    method addEntity (line 244) | func (w *ecsWorld) addEntity(info EntityInfo) *EntityInfo {
    method getEntityInfo (line 248) | func (w *ecsWorld) getEntityInfo(entity Entity) (*EntityInfo, bool) {
    method deleteEntity (line 252) | func (w *ecsWorld) deleteEntity(entity Entity) {
    method getComponentSet (line 256) | func (w *ecsWorld) getComponentSet(typ reflect.Type) IComponentSet {
    method getComponentSetByIntType (line 260) | func (w *ecsWorld) getComponentSetByIntType(it uint16) IComponentSet {
    method getComponentMetaInfoByType (line 264) | func (w *ecsWorld) getComponentMetaInfoByType(typ reflect.Type) *Compo...
    method getComponentCollection (line 268) | func (w *ecsWorld) getComponentCollection() IComponentCollection {
    method getComponentMeta (line 272) | func (w *ecsWorld) getComponentMeta() *componentMeta {
    method getOrCreateComponentMetaInfo (line 276) | func (w *ecsWorld) getOrCreateComponentMetaInfo(component IComponent) ...
    method newEntity (line 280) | func (w *ecsWorld) newEntity() *EntityInfo {
    method addComponent (line 285) | func (w *ecsWorld) addComponent(entity Entity, component IComponent) {
    method deleteComponent (line 293) | func (w *ecsWorld) deleteComponent(entity Entity, component IComponent) {
    method deleteComponentByIntType (line 297) | func (w *ecsWorld) deleteComponentByIntType(entity Entity, it uint16) {
    method addFreeComponent (line 301) | func (w *ecsWorld) addFreeComponent(component IComponent) {
    method checkMainThread (line 311) | func (w *ecsWorld) checkMainThread() {

FILE: world_async.go
  type SyncWrapper (line 8) | type SyncWrapper struct
    method getWorld (line 12) | func (g SyncWrapper) getWorld() IWorld {
    method NewEntity (line 16) | func (g SyncWrapper) NewEntity() Entity {
    method DestroyEntity (line 20) | func (g SyncWrapper) DestroyEntity(entity Entity) {
    method Add (line 28) | func (g SyncWrapper) Add(entity Entity, components ...IComponent) {
    method Remove (line 36) | func (g SyncWrapper) Remove(entity Entity, components ...IComponent) {
  type syncTask (line 44) | type syncTask struct
  type AsyncWorld (line 49) | type AsyncWorld struct
    method Startup (line 65) | func (w *AsyncWorld) Startup() {
    method Stop (line 98) | func (w *AsyncWorld) Stop() {
    method dispatch (line 102) | func (w *AsyncWorld) dispatch() {
    method Sync (line 126) | func (w *AsyncWorld) Sync(fn func(g SyncWrapper) error) {
    method Wait (line 136) | func (w *AsyncWorld) Wait(fn func(g SyncWrapper) error) {
  function NewAsyncWorld (line 57) | func NewAsyncWorld(config *WorldConfig) *AsyncWorld {

FILE: world_sync.go
  type SyncWorld (line 5) | type SyncWorld struct
    method Startup (line 15) | func (w *SyncWorld) Startup() {
    method Update (line 19) | func (w *SyncWorld) Update() {
    method Optimize (line 23) | func (w *SyncWorld) Optimize(t time.Duration, force bool) {}
    method Stop (line 25) | func (w *SyncWorld) Stop() {
    method NewEntity (line 29) | func (w *SyncWorld) NewEntity() Entity {
    method DestroyEntity (line 33) | func (w *SyncWorld) DestroyEntity(entity Entity) {
    method Add (line 41) | func (w *SyncWorld) Add(entity Entity, components ...IComponent) {
    method Remove (line 49) | func (w *SyncWorld) Remove(entity Entity, components ...IComponent) {
    method getWorld (line 57) | func (w *SyncWorld) getWorld() IWorld {
  function NewSyncWorld (line 9) | func NewSyncWorld(config *WorldConfig) *SyncWorld {

FILE: world_test.go
  constant __worldTest_Entity_Count (line 11) | __worldTest_Entity_Count int = 3
  type __world_Test_C_1 (line 14) | type __world_Test_C_1 struct
  type __world_Test_C_2 (line 21) | type __world_Test_C_2 struct
  type __world_Test_C_3 (line 28) | type __world_Test_C_3 struct
  type __world_Test_S_1 (line 34) | type __world_Test_S_1 struct
    method Init (line 38) | func (w *__world_Test_S_1) Init(si SystemInitConstraint) error {
    method Update (line 44) | func (w *__world_Test_S_1) Update(event Event) {
  type __world_Test_U_Input (line 67) | type __world_Test_U_Input struct
    method ChangeName (line 71) | func (u *__world_Test_U_Input) ChangeName(entity Entity, name string) {
  function Test_ecsWorld_World (line 81) | func Test_ecsWorld_World(t *testing.T) {
  function Test_ecsWorld_World_launcher (line 113) | func Test_ecsWorld_World_launcher(t *testing.T) {
Condensed preview — 85 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (258K chars).
[
  {
    "path": ".gitignore",
    "chars": 7,
    "preview": ".idea/\n"
  },
  {
    "path": "LICENSE",
    "chars": 1495,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2023, zllangct\n\nRedistribution and use in source and binary forms, with or without\nm"
  },
  {
    "path": "README.md",
    "chars": 21948,
    "preview": "# ECS\n这是一个ECS(Entity-Component-System)Go语言版本的实现,它聚焦于游戏领域的应用,帮助你快速构建一个高内聚、低耦合、易扩展、高性能的并行化游戏世界。\n## Contents\n* [快速开始](#快速开始"
  },
  {
    "path": "chunk.go",
    "chars": 2555,
    "preview": "package ecs\n\nconst (\n\tChunkSize uintptr = 1024 * 16\n\t//ChunkSize  int64   = 512\n\tEntitySize uintptr = 8\n)\n\nconst (\n\tChun"
  },
  {
    "path": "collection_iter.go",
    "chars": 1015,
    "preview": "package ecs\n\nimport \"unsafe\"\n\ntype Iterator[T any] interface {\n\tBegin() *T\n\tVal() *T\n\tNext() *T\n\tEnd() bool\n}\n\ntype Iter"
  },
  {
    "path": "component.go",
    "chars": 5546,
    "preview": "package ecs\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"unsafe\"\n)\n\nconst (\n\th4   = uint8(240)\n\tl4   = uint8(15)\n\tzero = uint8(0)\n)\n\nty"
  },
  {
    "path": "component_collection.go",
    "chars": 6605,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\ntype CollectionOperate uint8\n\nconst (\n\tCollectionOperateNone      "
  },
  {
    "path": "component_getter.go",
    "chars": 1640,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\ntype GetterCache struct {\n\tindices []reflect.Type\n\tvalues  []unsafe.Pointe"
  },
  {
    "path": "component_meta.go",
    "chars": 2998,
    "preview": "package ecs\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc GetComponentMeta[T ComponentObject](world IWorld) *ComponentMetaInfo {\n\t"
  },
  {
    "path": "component_meta_test.go",
    "chars": 526,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc BenchmarkIntTypeAndReflectType(b *testing.B) {\n\tinfos := NewSparseAr"
  },
  {
    "path": "component_operate_task.go",
    "chars": 1238,
    "preview": "package ecs\n\nimport (\n\t\"sync\"\n)\n\ntype opTask struct {\n\ttarget Entity\n\tcom    IComponent\n\top     CollectionOperate\n\tnext "
  },
  {
    "path": "component_set.go",
    "chars": 3782,
    "preview": "package ecs\n\nimport (\n\t\"sort\"\n\t\"unsafe\"\n)\n\ntype IComponentSet interface {\n\tLen() int\n\tRange(fn func(com IComponent) bool"
  },
  {
    "path": "component_set_test.go",
    "chars": 2089,
    "preview": "package ecs\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n)\n\nfunc TestComponentSet_Sort(t *testing.T) {\n\t//准备数据\n\tcaseCount := 50\n\tva"
  },
  {
    "path": "component_test.go",
    "chars": 1098,
    "preview": "package ecs\n\nimport \"testing\"\n\nfunc TestComponent_isValidComponentType(t *testing.T) {\n\ttype C1 struct {\n\t\tComponent[C1]"
  },
  {
    "path": "component_utils.go",
    "chars": 259,
    "preview": "package ecs\n\nimport (\n\t\"encoding/json\"\n\t\"reflect\"\n)\n\nfunc GetType[T ComponentObject]() reflect.Type {\n\treturn TypeOf[T]("
  },
  {
    "path": "compound.go",
    "chars": 184,
    "preview": "package ecs\n\ntype Compound = OrderedIntSet[uint16]\n\nfunc NewCompound(initCap ...int) Compound {\n\tcap := 0\n\tif len(initCa"
  },
  {
    "path": "compound_test.go",
    "chars": 3743,
    "preview": "package ecs\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc Test_getCompoundType(t *testing.T) {\n\treturn\n\tfilePath :="
  },
  {
    "path": "compound_utils.go",
    "chars": 16813,
    "preview": "package ecs\n\nimport (\n\t\"unsafe\"\n)\n\nfunc getCompoundType(compound Compound) interface{} {\n\tlength := len(compound)\n\tif le"
  },
  {
    "path": "concurrent_map.go",
    "chars": 4945,
    "preview": "package ecs\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\ntype Map[K comparable, V any] struct {\n\tmu     sync.Mutex\n\trea"
  },
  {
    "path": "concurrent_map_test.go",
    "chars": 1418,
    "preview": "// Copyright 2016 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "direct_api.go",
    "chars": 2038,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\nfunc RegisterSystem[T SystemObject, TP SystemPointer[T]](world IWorld, ord"
  },
  {
    "path": "entity.go",
    "chars": 2377,
    "preview": "package ecs\n\nimport (\n\t\"sort\"\n\t\"unsafe\"\n)\n\ntype Entity int64\n\nfunc (e Entity) ToInt64() int64 {\n\treturn int64(e)\n}\n\nfunc"
  },
  {
    "path": "entity_info.go",
    "chars": 1312,
    "preview": "package ecs\n\ntype EntityInfo struct {\n\tentity   Entity\n\tcompound Compound\n}\n\nfunc (e *EntityInfo) Destroy(world IWorld) "
  },
  {
    "path": "entity_set.go",
    "chars": 784,
    "preview": "package ecs\n\ntype EntitySet struct {\n\tSparseArray[int32, EntityInfo]\n}\n\nfunc NewEntityCollection() *EntitySet {\n\treturn "
  },
  {
    "path": "entity_test.go",
    "chars": 3003,
    "preview": "package ecs\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n)\n\nfunc TestEntityIDGenerator_NewID(t *testing.T) {\n\tt.R"
  },
  {
    "path": "example/benchmark-0/components.go",
    "chars": 1062,
    "preview": "package main\n\nimport (\n\t\"github.com/zllangct/ecs\"\n)\n\ntype Position struct {\n\tecs.Component[Position]\n\tX int\n\tY int\n\tZ in"
  },
  {
    "path": "example/benchmark-0/damage_system.go",
    "chars": 2309,
    "preview": "package main\n\nimport (\n\t\"errors\"\n\t\"github.com/zllangct/ecs\"\n\t\"math/rand\"\n)\n\ntype Caster struct {\n\tAction   *Action\n\tPosi"
  },
  {
    "path": "example/benchmark-0/game_common.go",
    "chars": 63,
    "preview": "package main\n\nconst (\n\tPlayerCount = 200\n\tDummyMaxFor = 3000\n)\n"
  },
  {
    "path": "example/benchmark-0/game_ecs.go",
    "chars": 1512,
    "preview": "package main\n\nimport (\n\t\"github.com/zllangct/ecs\"\n\t_ \"unsafe\"\n)\n\ntype GameECS struct {\n\tworld    *ecs.SyncWorld\n\tentitie"
  },
  {
    "path": "example/benchmark-0/game_normal.go",
    "chars": 7024,
    "preview": "package main\n\nimport (\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype Player struct {\n\trw sync.RWMutex\n\n\tID int64\n\n\tX             "
  },
  {
    "path": "example/benchmark-0/go.mod",
    "chars": 117,
    "preview": "module test_ecs_m_d\n\ngo 1.18\n\nreplace github.com/zllangct/ecs => ./../..\n\nrequire (\n\tgithub.com/zllangct/ecs v0.0.0\n)"
  },
  {
    "path": "example/benchmark-0/main_benchmark_test.go",
    "chars": 1579,
    "preview": "package main\n\nimport (\n\t\"github.com/zllangct/ecs\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc BenchmarkNormal(b *testing.B) {\n"
  },
  {
    "path": "example/benchmark-0/main_test.go",
    "chars": 1387,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/zllangct/ecs\"\n\t_ \"net/http/pprof\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\t_ "
  },
  {
    "path": "example/benchmark-0/move_system.go",
    "chars": 1005,
    "preview": "package main\n\nimport (\n\t\"github.com/zllangct/ecs\"\n)\n\ntype MoveSystem struct {\n\tecs.System[MoveSystem]\n}\n\nfunc (m *MoveSy"
  },
  {
    "path": "example/benchmark-0/simu_load_system.go",
    "chars": 3764,
    "preview": "package main\n\nimport \"github.com/zllangct/ecs\"\n\ntype Test1System struct {\n\tecs.System[Test1System]\n}\n\nfunc (t *Test1Syst"
  },
  {
    "path": "example/fake-simple-game-server/client/fake_client.go",
    "chars": 1095,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/zllangct/ecs\"\n\t\"math/rand\"\n\t\"test_ecs_fake_server/network\"\n\t\"tim"
  },
  {
    "path": "example/fake-simple-game-server/game/chat.go",
    "chars": 329,
    "preview": "package game\n\nimport (\n\t\"sync\"\n)\n\ntype ChatRoom struct {\n\tclients *sync.Map\n}\n\nfunc NewChatRoom(c *sync.Map) *ChatRoom {"
  },
  {
    "path": "example/fake-simple-game-server/game/empty_system.go",
    "chars": 787,
    "preview": "package game\n\nimport \"github.com/zllangct/ecs\"\n\ntype EmptySystem struct {\n\tecs.System[EmptySystem]\n\tisPreStart  bool\n\tis"
  },
  {
    "path": "example/fake-simple-game-server/game/fake_game.go",
    "chars": 3206,
    "preview": "package game\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"github.com/zllangct/ecs\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"test_ecs_fake_ser"
  },
  {
    "path": "example/fake-simple-game-server/game/move_component.go",
    "chars": 191,
    "preview": "package game\n\nimport \"github.com/zllangct/ecs\"\n\ntype Position struct {\n\tecs.Component[Position]\n\tX int\n\tY int\n\tZ int\n}\n\n"
  },
  {
    "path": "example/fake-simple-game-server/game/move_system.go",
    "chars": 1761,
    "preview": "package game\n\nimport (\n\t\"errors\"\n\t\"github.com/zllangct/ecs\"\n\t\"time\"\n)\n\ntype MoveSystemUtility struct {\n\tecs.Utility[Move"
  },
  {
    "path": "example/fake-simple-game-server/game/player_component.go",
    "chars": 160,
    "preview": "package game\n\nimport \"github.com/zllangct/ecs\"\n\ntype PlayerComponent struct {\n\tecs.Component[PlayerComponent]\n\tName     "
  },
  {
    "path": "example/fake-simple-game-server/game/position_sync_system.go",
    "chars": 625,
    "preview": "package game\n\nimport \"github.com/zllangct/ecs\"\n\ntype PlayerPosition struct {\n\tSessionID int\n\tPos       Position\n}\n\ntype "
  },
  {
    "path": "example/fake-simple-game-server/game/session.go",
    "chars": 174,
    "preview": "package game\n\nimport (\n\t\"github.com/zllangct/ecs\"\n\t\"test_ecs_fake_server/network\"\n)\n\ntype Session struct {\n\tSessionID in"
  },
  {
    "path": "example/fake-simple-game-server/gm/gm.go",
    "chars": 566,
    "preview": "package gm\n\nimport (\n\t\"context\"\n\t\"math/rand\"\n\t\"test_ecs_fake_server/game\"\n\t\"time\"\n)\n\ntype GM struct {\n\tgame *game.FakeGa"
  },
  {
    "path": "example/fake-simple-game-server/go.mod",
    "chars": 121,
    "preview": "module test_ecs_fake_server\n\ngo 1.18\n\nreplace github.com/zllangct/ecs => ./../..\n\nrequire github.com/zllangct/ecs v0.0.0"
  },
  {
    "path": "example/fake-simple-game-server/main.go",
    "chars": 554,
    "preview": "package main\n\nimport (\n\t\"context\"\n\t\"github.com/zllangct/ecs\"\n\t\"net/http\"\n\t_ \"net/http/pprof\"\n\t\"test_ecs_fake_server/clie"
  },
  {
    "path": "example/fake-simple-game-server/network/fake_net.go",
    "chars": 783,
    "preview": "package network\n\ntype TcpConn struct {\n\tr chan interface{}\n\tw chan interface{}\n}\n\nfunc NewTcpConn() *TcpConn {\n\treturn &"
  },
  {
    "path": "fixed_string.go",
    "chars": 719,
    "preview": "package ecs\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nconst (\n\t__FixedMax = 128\n)\n\ntype FixedString[T any] struct {\n\tdata T\n\tlen  in"
  },
  {
    "path": "fixed_string_test.go",
    "chars": 977,
    "preview": "package ecs\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestFixedString_Generate(t *testing.T) {\n\treturn\n\tfilePat"
  },
  {
    "path": "fixed_string_utils.go",
    "chars": 2869,
    "preview": "package ecs\n\ntype Fixed1 [1]byte\ntype Fixed2 [2]byte\ntype Fixed3 [3]byte\ntype Fixed4 [4]byte\ntype Fixed5 [5]byte\ntype Fi"
  },
  {
    "path": "go.mod",
    "chars": 40,
    "preview": "module github.com/zllangct/ecs\n\ngo 1.18\n"
  },
  {
    "path": "go.sum",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "goroutine_id.go",
    "chars": 1405,
    "preview": "// Copyright ©2020 Dan Kortschak. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "goroutine_pool.go",
    "chars": 2011,
    "preview": "package ecs\n\nimport (\n\truntime2 \"runtime\"\n)\n\n// Worker goroutine struct.\ntype Worker struct {\n\tp        *Pool\n\tjobQueue "
  },
  {
    "path": "goroutine_pool_benchmark_test.go",
    "chars": 809,
    "preview": "package ecs\n\nimport (\n\t\"sync\"\n\t\"testing\"\n)\n\nconst (\n\trunTimes  = 25\n\tpoolSize  = 12\n\tqueueSize = 50\n)\n\nfunc demoTask() {"
  },
  {
    "path": "internal_type_mock.go",
    "chars": 2331,
    "preview": "package ecs\n\nimport (\n\t\"unsafe\"\n)\n\n// Signed is a constraint that permits any signed integer type.\n// If future releases"
  },
  {
    "path": "logger.go",
    "chars": 2247,
    "preview": "package ecs\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"runtime\"\n)\n\ntype Logger interface {\n\tDebug(v ...interface{})\n\tInfo(v ...inte"
  },
  {
    "path": "metrics.go",
    "chars": 1677,
    "preview": "package ecs\n\nimport \"time\"\n\ntype Metrics struct {\n\tenable  bool\n\tisPrint bool\n\tm       map[string]*MetricReporter\n}\n\nfun"
  },
  {
    "path": "optimizer.go",
    "chars": 3817,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"time\"\n)\n\ntype ShapeInfo struct {\n\ttyp    reflect.Type\n\teNum   int64\n\tshapes ["
  },
  {
    "path": "optimizer_benchmark_test.go",
    "chars": 2547,
    "preview": "package ecs\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\ttestOptimizerDummyMaxFor = 10\n\ttestOptimizerEntityMax "
  },
  {
    "path": "ordered_int_set.go",
    "chars": 1416,
    "preview": "package ecs\n\ntype OrderedIntSet[T Integer] []T\n\nfunc (c *OrderedIntSet[T]) InsertIndex(it T) int {\n\tif len(*c) == 0 {\n\t\t"
  },
  {
    "path": "ordered_int_set_test.go",
    "chars": 2323,
    "preview": "package ecs\n\nimport \"testing\"\n\nfunc TestOrderedIntSet_Add(t *testing.T) {\n\tc := OrderedIntSet[uint16]{}\n\tinsert := []uin"
  },
  {
    "path": "serialize.go",
    "chars": 112,
    "preview": "package ecs\n\n// TODO 世界的序列化、反序列化\ntype ICustomSerialize interface {\n\tSerialize() []byte\n\tDeSerialize(b []byte)\n}\n"
  },
  {
    "path": "shape_getter.go",
    "chars": 3635,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\ntype IShape interface {\n\tbase() *shapeBase\n\tgetType() reflect.Type\n}\n\ntype"
  },
  {
    "path": "shape_getter_test.go",
    "chars": 1353,
    "preview": "package ecs\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\ntype __ShapeGetter_Test_C_1 struct {\n\tComponent[__ShapeGetter_Test_C_1]\n\tFie"
  },
  {
    "path": "shape_iter.go",
    "chars": 2375,
    "preview": "package ecs\n\nimport \"unsafe\"\n\ntype IShapeIterator[T any] interface {\n\tBegin() *T\n\tVal() *T\n\tNext() *T\n\tEnd() bool\n}\n\ntyp"
  },
  {
    "path": "sparse_array.go",
    "chars": 2963,
    "preview": "package ecs\n\ntype SparseArray[K Integer, V any] struct {\n\tUnorderedCollection[V]\n\tindices         []int32\n\tidx2Key      "
  },
  {
    "path": "system.go",
    "chars": 5798,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\ntype SystemState uint8\n\nconst (\n\tSystemStateInvalid SystemState = "
  },
  {
    "path": "system_event.go",
    "chars": 1240,
    "preview": "package ecs\n\nimport \"time\"\n\ntype Event struct {\n\tFrame uint64\n\tDelta time.Duration\n}\n\ntype InitReceiver interface {\n\tIni"
  },
  {
    "path": "system_flow.go",
    "chars": 11700,
    "preview": "package ecs\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n)\n\nconst (\n\tStageSyncBeforeStart Stage = iota\n\tStageStart\n\tStageSyncAfte"
  },
  {
    "path": "system_group.go",
    "chars": 5897,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n)\n\nvar emptySystemGroupIterator = &SystemGroupIterator{}\n\n// Node system tree n"
  },
  {
    "path": "system_group_test.go",
    "chars": 3785,
    "preview": "package ecs\n\nimport (\n\t\"testing\"\n)\n\ntype __systemGroup_Test_C_1 struct {\n\tComponent[__systemGroup_Test_C_1]\n}\ntype __sys"
  },
  {
    "path": "system_requirement.go",
    "chars": 648,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\nconst (\n\tComponentReadWrite ComponentPermission = 0\n\tComponentReadOnly  Co"
  },
  {
    "path": "unordered_collection.go",
    "chars": 2671,
    "preview": "package ecs\n\nimport (\n\t\"unsafe\"\n)\n\nconst (\n\tInitMaxSize = 1024 * 16\n\t//InitMaxSize        = 0\n\tSeqMax uint32 = 0xFFFFFFF"
  },
  {
    "path": "unordered_collection_test.go",
    "chars": 3735,
    "preview": "package ecs\n\nimport (\n\t\"testing\"\n\t\"unsafe\"\n)\n\ntype __unorderedCollection_Test_item struct {\n\tComponent[__unorderedCollec"
  },
  {
    "path": "unordered_collection_with_id.go",
    "chars": 2605,
    "preview": "package ecs\n\nimport \"unsafe\"\n\ntype ICollectionWithID interface {\n\tLen() int\n\tClear()\n\tRange(func(v *any) bool)\n\tGetByInd"
  },
  {
    "path": "unordered_collection_with_id_test.go",
    "chars": 1705,
    "preview": "package ecs\n\nimport (\n\t\"testing\"\n)\n\nfunc BenchmarkUnorderedCollectionWithID_Read(b *testing.B) {\n\tc := NewUnorderedColle"
  },
  {
    "path": "utility.go",
    "chars": 1243,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\ntype IUtilityGetter interface {\n\tgetWorld() IWorld\n}\n\ntype UtilityGetter s"
  },
  {
    "path": "utils.go",
    "chars": 2372,
    "preview": "package ecs\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"runtime/debug\"\n\t\"sync/atomic\"\n\t\"time\"\n\t\"unsafe\"\n)\n\nvar Empty s"
  },
  {
    "path": "utils_test.go",
    "chars": 391,
    "preview": "package ecs\n\nimport (\n\t\"testing\"\n)\n\nfunc TestUniqueID(t *testing.T) {\n\tm := make(map[int64]struct{})\n\tcount := 0\n\tfor i "
  },
  {
    "path": "world.go",
    "chars": 7906,
    "preview": "package ecs\n\nimport (\n\t\"reflect\"\n\t\"runtime\"\n\t\"sync/atomic\"\n\t\"time\"\n\t\"unsafe\"\n)\n\ntype WorldStatus int\n\nconst (\n\tWorldStat"
  },
  {
    "path": "world_async.go",
    "chars": 2518,
    "preview": "package ecs\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype SyncWrapper struct {\n\tworld *IWorld\n}\n\nfunc (g SyncWrapper) getWorld() IWo"
  },
  {
    "path": "world_sync.go",
    "chars": 949,
    "preview": "package ecs\n\nimport \"time\"\n\ntype SyncWorld struct {\n\tecsWorld\n}\n\nfunc NewSyncWorld(config *WorldConfig) *SyncWorld {\n\tw "
  },
  {
    "path": "world_test.go",
    "chars": 3434,
    "preview": "package ecs\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\t__worldTest_Entity_Count int = 3\n)\n\ntype __world_"
  }
]

About this extraction

This page contains the full source code of the zllangct/ecs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 85 files (208.8 KB), approximately 70.2k tokens, and a symbol index with 942 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!