diff --git a/source/game/BaseAnimating.h b/source/game/BaseAnimating.h
index d8d7d9ac88745073cb4d2a639ed3f24eab0fbf11..e5336bcd4de9646f8c99ca3cb9dcd20f331e9b19 100644
--- a/source/game/BaseAnimating.h
+++ b/source/game/BaseAnimating.h
@@ -74,8 +74,6 @@ public:
 	float3 getAttachmentPos(int id) override;
 	SMQuaternion getAttachmentRot(int id) override;
 
-	//void onSync() override;
-
 	void playAnimation(const char * name, UINT uFadeTime = 0, UINT slot = 0);
 	void playActivity(const char * name, UINT uFadeTime = 0, UINT slot = 0);
 
diff --git a/source/game/BaseCharacter.cpp b/source/game/BaseCharacter.cpp
index 93f558cbad7312bd0f4ac4bb9436aefe607018e9..859ba65d862ec8deed5fb3578470da99f6819fff 100644
--- a/source/game/BaseCharacter.cpp
+++ b/source/game/BaseCharacter.cpp
@@ -21,6 +21,8 @@ END_PROPTABLE()
 
 REGISTER_ENTITY_NOLISTING(CBaseCharacter, base_character);
 
+IEventChannel<XEventPhysicsStep> *CBaseCharacter::m_pTickEventChannel = NULL;
+
 class btKinematicClosestNotMeRayResultCallback: public btCollisionWorld::ClosestRayResultCallback
 {
 public:
@@ -45,6 +47,20 @@ protected:
 	btCollisionObject* m_me;
 };
 
+void CCharacterPhysicsTickEventListener::onEvent(const XEventPhysicsStep *pData)
+{
+	m_pCharacter->onPhysicsStep();
+}
+
+CBaseCharacter::CBaseCharacter():
+	m_physicsTicker(this)
+{
+	if(!m_pTickEventChannel)
+	{
+		m_pTickEventChannel = Core_GetIXCore()->getEventChannel<XEventPhysicsStep>(EVENT_PHYSICS_STEP_GUID);
+	}
+}
+
 void CBaseCharacter::onPostLoad()
 {
 	BaseClass::onPostLoad();
@@ -114,10 +130,14 @@ void CBaseCharacter::onPostLoad()
 	m_idTaskSpread = SET_INTERVAL(updateSpread, 1.0f / 30.0f);
 
 	m_pInventory = new CCharacterInventory(this);
+
+	m_pTickEventChannel->addListener(&m_physicsTicker);
 }
 
 CBaseCharacter::~CBaseCharacter()
 {
+	m_pTickEventChannel->removeListener(&m_physicsTicker);
+
 	CLEAR_INTERVAL(m_idTaskSpread);
 	REMOVE_ENTITY(m_pHeadEnt);
 	REMOVE_ENTITY(m_flashlight);
@@ -428,10 +448,15 @@ void CBaseCharacter::releaseHitboxes()
 	mem_delete_a(m_pHitboxBodies);
 }
 
-void CBaseCharacter::onSync()
+float3 CBaseCharacter::getHeadOffset()
 {
-	BaseClass::onSync();
+	float3 vHeadOffset; // = m_pHeadEnt->getOffsetPos();
+	vHeadOffset.y = m_fCapsHeight * m_fCurrentHeight - 0.1f;
+	return(vHeadOffset);
+}
 
+void CBaseCharacter::onPhysicsStep()
+{
 	updateHitboxes();
 
 	if(m_uMoveDir & PM_OBSERVER)
@@ -439,13 +464,11 @@ void CBaseCharacter::onSync()
 		return;
 	}
 	btTransform &trans = m_pGhostObject->getWorldTransform();
-	m_vPosition = (float3)(float3(trans.getOrigin().x(), trans.getOrigin().y() - m_fCapsHeight * m_fCurrentHeight * 0.5f, trans.getOrigin().z()));
-
-	float3 vHeadOffset = m_pHeadEnt->getOffsetPos();
-	vHeadOffset.y = m_fCapsHeight * m_fCurrentHeight - 0.1f;
-	m_pHeadEnt->setOffsetPos(vHeadOffset);
+	setPos(float3(trans.getOrigin().x(), trans.getOrigin().y() - m_fCapsHeight * m_fCurrentHeight * 0.5f, trans.getOrigin().z()));
 
+	m_pHeadEnt->setOffsetPos(getHeadOffset());
 
+#if 0
 	//находим текущий квад аи сетки на котором находится игрок
 	ID idq = SAIG_QuadGet(&float3(m_vPosition), true);
 	//если нашли
@@ -468,6 +491,7 @@ void CBaseCharacter::onSync()
 
 		m_idQuadCurr = idq;
 	}
+#endif
 }
 
 void CBaseCharacter::initPhysics()
diff --git a/source/game/BaseCharacter.h b/source/game/BaseCharacter.h
index 71f6b88e4a01f7e9748c39aa7bdfde6cc3c3a246..6843723365c5e49fbefdee70ca99b026876a10f2 100644
--- a/source/game/BaseCharacter.h
+++ b/source/game/BaseCharacter.h
@@ -42,13 +42,29 @@ enum PLAYER_MOVE
 
 class CHUDcontroller;
 
+class CBaseCharacter;
+class CCharacterPhysicsTickEventListener final: public IEventListener<XEventPhysicsStep>
+{
+public:
+	CCharacterPhysicsTickEventListener(CBaseCharacter *pTrigger):
+		m_pCharacter(pTrigger)
+	{
+	}
+	void onEvent(const XEventPhysicsStep *pData) override;
+
+private:
+	CBaseCharacter *m_pCharacter;
+};
+
 //! Класс игрока  \ingroup cbaseanimating
 class CBaseCharacter: public CBaseAnimating
 {
 	DECLARE_CLASS(CBaseCharacter, CBaseAnimating);
 	DECLARE_PROPTABLE();
+
+	friend class CCharacterPhysicsTickEventListener;
 public:
-	DECLARE_TRIVIAL_CONSTRUCTOR();
+	DECLARE_CONSTRUCTOR();
 	~CBaseCharacter();
 
 	//! Запускает/останавливает первичную атаку
@@ -83,8 +99,6 @@ public:
 	void releaseHitboxes();
 	void updateHitboxes();
 
-	void onSync() override;
-
 	void initPhysics();
 	void releasePhysics();
 
@@ -167,6 +181,13 @@ protected:
 	float m_fCurrentHeight = 1.0f;
 
 	IXResourceModelAnimated *m_pHandsModelResource = NULL;
+
+	virtual float3 getHeadOffset();
+
+private:
+	static IEventChannel<XEventPhysicsStep> *m_pTickEventChannel;
+	CCharacterPhysicsTickEventListener m_physicsTicker;
+	void onPhysicsStep();
 };
 
 #endif
diff --git a/source/game/BaseEntity.cpp b/source/game/BaseEntity.cpp
index 6f7ff03ccb874d6eeac8470c6a458ce8a8a113c3..d9156c7f4636497057e23d060046f4ed7cb8065b 100644
--- a/source/game/BaseEntity.cpp
+++ b/source/game/BaseEntity.cpp
@@ -197,11 +197,32 @@ void CBaseEntity::setOrient(const SMQuaternion & q)
 void CBaseEntity::setOffsetOrient(const SMQuaternion & q)
 {
 	m_qOffsetOrient = q;
+	onParentMoved();
 }
 
 void CBaseEntity::setOffsetPos(const float3 & pos)
 {
 	m_vOffsetPos = pos;
+	onParentMoved();
+}
+
+void CBaseEntity::setXform(const float3 &vPos, const SMQuaternion &q)
+{
+	bool bOld = m_isInOnParentMoved;
+	m_isInOnParentMoved = true;
+	setPos(vPos);
+	m_isInOnParentMoved = bOld;
+
+	setOrient(q);
+}
+void CBaseEntity::setOffsetXform(const float3 &vPos, const SMQuaternion &q)
+{
+	bool bOld = m_isInOnParentMoved;
+	m_isInOnParentMoved = true;
+	setOffsetPos(vPos);
+	m_isInOnParentMoved = bOld;
+
+	setOffsetOrient(q);
 }
 
 float3 CBaseEntity::getOffsetPos()
@@ -267,7 +288,6 @@ bool CBaseEntity::setKV(const char * name, const char * value)
 	SMQuaternion q;
 	int d;
 	float f;
-	CBaseEntity * pEnt;
 	switch(field->type)
 	{
 	case PDF_INT:
@@ -487,7 +507,6 @@ bool CBaseEntity::getKV(const char * name, char * out, int bufsize)
 	}
 	SMQuaternion q;
 	float3_t f3;
-	CBaseEntity *pEnt;
 	switch(field->type)
 	{
 	case PDF_INT:
diff --git a/source/game/BaseEntity.h b/source/game/BaseEntity.h
index ad35a5c17fa515d1560c67e8b2bc7b89d5410e00..a53dd17699a90a6df2cd24d59086e23af51c6c33 100644
--- a/source/game/BaseEntity.h
+++ b/source/game/BaseEntity.h
@@ -89,6 +89,9 @@ public:
 	//! Возвращает вращение объекта
 	SMQuaternion getOrient();
 
+	void setXform(const float3 &vPos, const SMQuaternion &q);
+	void setOffsetXform(const float3 &vPos, const SMQuaternion &q);
+
 	//! Устанавливает свойство объекта
 	virtual bool setKV(const char *name, const char *value);
 	//! Получает свойство объекта
@@ -224,10 +227,6 @@ protected:
 	//! Владелец
 	CEntityPointer<CBaseEntity> m_pOwner;
 
-	//! Вызывается на стадии синхронизации
-	virtual void onSync()
-	{
-	}
 	//! Вызывается при создании после установки всех свойств
 	virtual void onPostLoad();
 
diff --git a/source/game/BaseTool.cpp b/source/game/BaseTool.cpp
index bbf92e0e6520a533b46bf68874e07e8a5b14ced1..f68dcd7e72b2927d1958bf81afa93eac27287c5a 100644
--- a/source/game/BaseTool.cpp
+++ b/source/game/BaseTool.cpp
@@ -232,8 +232,11 @@ void CBaseTool::dbgMove(int dir, float dy)
 			, m_fCenterLength);
 		break;
 	}
+
+	updateTransform();
 }
 
+#if 0
 void CBaseTool::onSync()
 {
 	if(m_pOwner)
@@ -256,6 +259,18 @@ void CBaseTool::onSync()
 		SPE_EffectSetRotQ(m_iMuzzleFlash, m_vOrientation);
 	}
 }
+#endif
+
+void CBaseTool::setShakeRotation(const SMQuaternion &q)
+{
+	m_qShakeRotation = q;
+	updateTransform();
+}
+
+void CBaseTool::updateTransform()
+{
+	setOffsetXform(m_vSlotPosResult, m_qSlotRotResult * m_qShakeRotation);
+}
 
 void CBaseTool::_update(float dt)
 {
@@ -346,6 +361,7 @@ void CBaseTool::_rezoom()
 	{
 		((CPlayer*)m_pOwner.getEntity())->getCamera()->getCamera()->setFOV(SMToRadian(vlerp(*r_default_fov, *r_default_fov - 10.0f, m_fZoomProgress)));
 	}
+	updateTransform();
 }
 
 bool CBaseTool::isWeapon() const
diff --git a/source/game/BaseTool.h b/source/game/BaseTool.h
index 5689a976495505ed9c32b6566cba81e62bc3e24f..bd6dcb86977261a661cae9fe0328413d74caa484 100644
--- a/source/game/BaseTool.h
+++ b/source/game/BaseTool.h
@@ -54,8 +54,6 @@ public:
 
 	void dbgMove(int dir, float delta);
 
-	void onSync() override;
-
 	void setParent(CBaseEntity * pEnt, int attachment = -1);
 
 	//! Этот инструмент - оружие
@@ -77,6 +75,8 @@ public:
 
 	virtual void updateHUDinfo();
 
+	void setShakeRotation(const SMQuaternion &q);
+
 protected:
 
 	bool isValidAmmo(CBaseSupply *pAmmo);
@@ -104,6 +104,8 @@ protected:
 	void _update(float dt);
 	void _rezoom();
 
+	void updateTransform();
+
 	float m_fReloadTime = 0.0f;
 
 	float3_t m_vSlotPos;
@@ -138,6 +140,8 @@ protected:
 
 	//! Этот инструмент - оружие
 	bool m_bIsWeapon = false;
+
+	SMQuaternion m_qShakeRotation;
 };
 
 #endif
diff --git a/source/game/EditorOutputsTab.cpp b/source/game/EditorOutputsTab.cpp
index fe0a6236d8514b85cd27783f4251293cc78544e8..0dbf3aac286801dac50c9db9cf0fdcd6a9aaa619 100644
--- a/source/game/EditorOutputsTab.cpp
+++ b/source/game/EditorOutputsTab.cpp
@@ -594,7 +594,7 @@ INT_PTR CALLBACK CEditorOutputsTab::dlgProc(HWND hWnd, UINT msg, WPARAM wParam,
 				if(pNMLV->hdr.idFrom == IDC_LIST_OUTPUTS && !m_isSkipUpdate && (pNMLV->uChanged & LVIF_STATE) && ((pNMLV->uNewState ^ pNMLV->uOldState) & LVIS_SELECTED))
 				{
 					Row *pRow = &m_pCurrentCommand->m_aRows[pNMLV->iItem];
-					pRow->isSelected = pNMLV->uNewState & LVIS_SELECTED;
+					pRow->isSelected = (pNMLV->uNewState & LVIS_SELECTED) != 0;
 
 					updateButtons();
 				}
diff --git a/source/game/EntityFactory.cpp b/source/game/EntityFactory.cpp
index d78952205131eac8ee1cea4167093214540c8cb9..fcfa2eb693c8ad01247ef3651741726f32faf4d5 100644
--- a/source/game/EntityFactory.cpp
+++ b/source/game/EntityFactory.cpp
@@ -61,11 +61,6 @@ CBaseEntity* CEntityFactoryMap::create(const char *szName, CEntityManager *pWorl
 			pEnt->_initEditorBoxes();
 		}
 
-		if(pFactory->isSyncable())
-		{
-			pWorld->regSync(pEnt);
-		}
-
 		return(pEnt);
 	}
 	return(NULL);
@@ -77,10 +72,6 @@ void CEntityFactoryMap::destroy(CBaseEntity *pEnt)
 		IEntityFactory *pFactory = getFactory(pEnt->getClassName());
 		if(pFactory)
 		{
-			if(pFactory->isSyncable())
-			{
-				pEnt->m_pMgr->unregSync(pEnt);
-			}
 			return(pFactory->destroy(pEnt));
 		}
 	}
diff --git a/source/game/EntityFactory.h b/source/game/EntityFactory.h
index 8f995d9412165266570a44459addd137f8825d6b..bde74bf914ec1c024c8986bcd8ff38e83d0bd019 100644
--- a/source/game/EntityFactory.h
+++ b/source/game/EntityFactory.h
@@ -29,7 +29,6 @@ public:
 	virtual bool isEditorHidden() = 0;
 	virtual EntDefaultsMap* getDefaults() = 0;
 	virtual IEntityFactory* copy(const char * szName, bool showInListing) = 0;
-	virtual bool isSyncable() = 0;
 };
 
 class CEntityFactoryMap
@@ -86,12 +85,11 @@ template <class T>
 class CEntityFactory: public IEntityFactory
 {
 public:
-	CEntityFactory(const char * szName, bool showInListing, bool isSyncable=true)
+	CEntityFactory(const char * szName, bool showInListing)
 	{
 		m_bShowInListing = showInListing;
 		m_szClassName = szName;
 		m_pPropTable = T::SGetPropTable();
-		m_isSyncable = isSyncable;
 		CEntityFactoryMap::GetInstance()->addFactory(this, szName);
 	}
 
@@ -141,11 +139,6 @@ public:
 		return(!m_bShowInListing);
 	}
 
-	bool isSyncable() override
-	{
-		return(m_isSyncable);
-	}
-
 	EntDefaultsMap* getDefaults() override
 	{
 		return(&m_mDefaults);
@@ -155,7 +148,6 @@ private:
 	const char * m_szClassName;
 	proptable_t * m_pPropTable;
 	bool m_bShowInListing;
-	bool m_isSyncable;
 	EntDefaultsMap m_mDefaults;
 	Array<CEntityFactory<T>*> m_vDerivatives;
 };
@@ -163,9 +155,6 @@ private:
 #define REGISTER_ENTITY(cls, name) \
 	CEntityFactory<cls> ent_ ## name ## _factory(#name, 1)
 
-#define REGISTER_ENTITY_NOSYNC(cls, name) \
-	CEntityFactory<cls> ent_ ## name ## _factory(#name, 1, 0)
-
 #define REGISTER_ENTITY_NOLISTING(cls, name) \
 	CEntityFactory<cls> ent_ ## name ## _factory(#name, 0)
 
diff --git a/source/game/EntityManager.cpp b/source/game/EntityManager.cpp
index 0317024cd2de051a5a0880ec26c6f1ab9619c49e..cd1da1ffa179a6fc7f440c0363d724de1ab027bf 100644
--- a/source/game/EntityManager.cpp
+++ b/source/game/EntityManager.cpp
@@ -167,8 +167,6 @@ void CEntityManager::finalRemove()
 
 void CEntityManager::sync()
 {
-	CBaseEntity * pEnt;
-
 	finalRemove();
 
 	for(int i = 0, l = m_vTimeout.size(); i < l; ++i)
@@ -200,16 +198,6 @@ void CEntityManager::sync()
 	//float dt;
 	//dt = (float)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - tOld).count() / 1000000.0f;
 	
-	for(int i = 0, l = m_vEntSyncList.size(); i < l; ++i)
-	{
-		pEnt = m_vEntSyncList[i];
-		if(pEnt)
-		{
-			//pEnt->updateDiscreteLinearVelocity(0, dt);
-			pEnt->onSync();
-			//pEnt->updateDiscreteLinearVelocity(1, dt);
-		}
-	}
 	//tOld = std::chrono::high_resolution_clock::now();
 }
 
@@ -315,19 +303,6 @@ void CEntityManager::unreg(CBaseEntity *pEnt)
 	m_mEnts.erase(*pEnt->getGUID());
 }
 
-void CEntityManager::regSync(CBaseEntity *pEnt)
-{
-	assert(pEnt);
-	m_vEntSyncList.push_back(pEnt);
-}
-void CEntityManager::unregSync(CBaseEntity *pEnt)
-{
-	assert(pEnt);
-	int iKey = m_vEntSyncList.indexOf(pEnt);
-	assert(iKey >= 0);
-	m_vEntSyncList.erase(iKey);
-}
-
 ID CEntityManager::setTimeout(void(CBaseEntity::*func)(float dt), CBaseEntity *pEnt, float delay)
 {
 	timeout_t t;
diff --git a/source/game/EntityManager.h b/source/game/EntityManager.h
index 7355d911ade294ab59460ab7a1b78634278163df..373d7a7698693421532aeb72a32f27c63614208a 100644
--- a/source/game/EntityManager.h
+++ b/source/game/EntityManager.h
@@ -149,12 +149,8 @@ protected:
 	const XGUID* reg(CBaseEntity *pEnt, const XGUID *pGUID=NULL);
 	void unreg(CBaseEntity *pEnt);
 
-	void regSync(CBaseEntity *pEnt);
-	void unregSync(CBaseEntity *pEnt);
-
 	Map<XGUID, CBaseEntity*> m_mEnts;
 	Array<CBaseEntity*, 64> m_vEntList;
-	Array<CBaseEntity*, 64> m_vEntSyncList;
 	Array<CBaseEntity*> m_vEntRemoveList;
 	Array<UINT> m_vFreeIDs;
 
diff --git a/source/game/Player.cpp b/source/game/Player.cpp
index d07b3b580cfe5ce78063c96525d4679a76db02e0..13b47219c88b435a0c146426562ea5c791a94374 100644
--- a/source/game/Player.cpp
+++ b/source/game/Player.cpp
@@ -95,7 +95,8 @@ void CPlayer::updateInput(float dt)
 		return;
 	}
 
-	m_vWpnShakeAngles = (float3)(m_vWpnShakeAngles * 0.4f);
+	// m_vWpnShakeAngles = (float3)(m_vWpnShakeAngles * 0.4f);
+	m_vWpnShakeAngles = (float3)(m_vWpnShakeAngles / (1.05f + dt));
 
 	if(!*editor_mode || SSInput_GetKeyState(SIM_LBUTTON))
 	{
@@ -313,6 +314,8 @@ void CPlayer::updateInput(float dt)
 
 	}
 
+	m_pActiveTool->setShakeRotation(SMQuaternion(m_vWpnShakeAngles.x, 'x') * SMQuaternion(m_vWpnShakeAngles.y, 'y') * SMQuaternion(m_vWpnShakeAngles.z, 'z'));
+
 #ifndef _SERVER
 	if(*grab_cursor && (!*editor_mode || SSInput_GetKeyState(SIM_LBUTTON)))
 	{
@@ -345,17 +348,17 @@ CPointCamera * CPlayer::getCamera()
 	return(m_pCamera);
 }
 
-void CPlayer::onSync()
+float3 CPlayer::getHeadOffset()
 {
-	BaseClass::onSync();
+	float3 vOffset = BaseClass::getHeadOffset();
 
-	if(m_uMoveDir & PM_OBSERVER)
+	if(!(m_uMoveDir & PM_OBSERVER))
 	{
-		return;
+		vOffset.y += m_fViewbobY;
+		vOffset += m_fViewbobStrafe;
 	}
 
-	m_vPosition.y += m_fViewbobY;
-	m_vPosition = (float3)(m_vPosition + m_fViewbobStrafe);
+	return(vOffset);
 }
 
 void CPlayer::observe()
diff --git a/source/game/Player.h b/source/game/Player.h
index c85778560b17befa0e9463727df07c9b37d653b7..9b4f3f7cc804a81de74695db152a44e3b346dcff 100644
--- a/source/game/Player.h
+++ b/source/game/Player.h
@@ -46,8 +46,6 @@ public:
 
 	//! Обновляет инпут от игрока
 	virtual void updateInput(float dt);
-
-	void onSync() override;
 	
 	//! Получает смещения для задержки движения модели оружия при вращении игрока
 	float3_t & getWeaponDeltaAngles();
@@ -105,6 +103,8 @@ protected:
 	void onPostLoad() override;
 
 	bool m_bCanRespawn = false;
+
+	float3 getHeadOffset() override;
 };
 
 #endif
diff --git a/source/game/PropStatic.cpp b/source/game/PropStatic.cpp
index 208bb7caaa9c125d4d4ed7119e451cd518bcc1fe..4305c48ba0f79244e6f614ddac5980e719a66553 100644
--- a/source/game/PropStatic.cpp
+++ b/source/game/PropStatic.cpp
@@ -18,7 +18,7 @@ BEGIN_PROPTABLE(CPropStatic)
 	EDITOR_COMBO_END()
 END_PROPTABLE()
 
-REGISTER_ENTITY_NOSYNC(CPropStatic, prop_static);
+REGISTER_ENTITY(CPropStatic, prop_static);
 
 CPropStatic::CPropStatic()
 {