Ejemplo n.º 1
0
/*!
 * \brief Main interaction loop for the recovery manager
 *
 * The recovery light-weight process only runs when you're the
 * synchronization site.  It performs the following tasks, if and only
 * if the prerequisite tasks have been performed successfully (it
 * keeps track of which ones have been performed in its bit map,
 * \p urecovery_state).
 *
 * First, it is responsible for probing that all servers are up.  This
 * is the only operation that must be performed even if this is not
 * yet the sync site, since otherwise this site may not notice that
 * enough other machines are running to even elect this guy to be the
 * sync site.
 *
 * After that, the recovery process does nothing until the beacon and
 * voting modules manage to get this site elected sync site.
 *
 * After becoming sync site, recovery first attempts to find the best
 * database available in the network (it must do this in order to
 * ensure finding the latest committed data).  After finding the right
 * database, it must fetch this dbase to the sync site.
 *
 * After fetching the dbase, it relabels it with a new version number,
 * to ensure that everyone recognizes this dbase as the most recent
 * dbase.
 *
 * One the dbase has been relabelled, this machine can start handling
 * requests.  However, the recovery module still has one more task:
 * propagating the dbase out to everyone who is up in the network.
 */
void *
urecovery_Interact(void *dummy)
{
    afs_int32 code, tcode;
    struct ubik_server *bestServer = NULL;
    struct ubik_server *ts;
    int dbok, doingRPC, now;
    afs_int32 lastProbeTime;
    /* if we're the sync site, the best db version we've found yet */
    static struct ubik_version bestDBVersion;
    struct ubik_version tversion;
    struct timeval tv;
    int length, tlen, offset, file, nbytes;
    struct rx_call *rxcall;
    char tbuffer[1024];
    struct ubik_stat ubikstat;
    struct in_addr inAddr;
    char hoststr[16];
    char pbuffer[1028];
    int fd = -1;
    afs_int32 pass;

    afs_pthread_setname_self("recovery");

    /* otherwise, begin interaction */
    urecovery_state = 0;
    lastProbeTime = 0;
    while (1) {
	/* Run through this loop every 4 seconds */
	tv.tv_sec = 4;
	tv.tv_usec = 0;
#ifdef AFS_PTHREAD_ENV
	select(0, 0, 0, 0, &tv);
#else
	IOMGR_Select(0, 0, 0, 0, &tv);
#endif

	ubik_dprint("recovery running in state %x\n", urecovery_state);

	/* Every 30 seconds, check all the down servers and mark them
	 * as up if they respond. When a server comes up or found to
	 * not be current, then re-find the the best database and
	 * propogate it.
	 */
	if ((now = FT_ApproxTime()) > 30 + lastProbeTime) {

	    for (ts = ubik_servers, doingRPC = 0; ts; ts = ts->next) {
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    doingRPC = 1;
		    code = DoProbe(ts);
		    if (code == 0) {
			UBIK_BEACON_LOCK;
			ts->up = 1;
			UBIK_BEACON_UNLOCK;
			DBHOLD(ubik_dbase);
			urecovery_state &= ~UBIK_RECFOUNDDB;
			DBRELE(ubik_dbase);
		    }
		} else {
		    UBIK_BEACON_UNLOCK;
		    DBHOLD(ubik_dbase);
		    if (!ts->currentDB)
			urecovery_state &= ~UBIK_RECFOUNDDB;
		    DBRELE(ubik_dbase);
		}
	    }

	    if (doingRPC)
		now = FT_ApproxTime();
	    lastProbeTime = now;
	}

	/* Mark whether we are the sync site */
	DBHOLD(ubik_dbase);
	if (!ubeacon_AmSyncSite()) {
	    urecovery_state &= ~UBIK_RECSYNCSITE;
	    DBRELE(ubik_dbase);
	    continue;		/* nothing to do */
	}
	urecovery_state |= UBIK_RECSYNCSITE;

	/* If a server has just come up or if we have not found the
	 * most current database, then go find the most current db.
	 */
	if (!(urecovery_state & UBIK_RECFOUNDDB)) {
	    DBRELE(ubik_dbase);
	    bestServer = (struct ubik_server *)0;
	    bestDBVersion.epoch = 0;
	    bestDBVersion.counter = 0;
	    for (ts = ubik_servers; ts; ts = ts->next) {
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    continue;	/* don't bother with these guys */
		}
		UBIK_BEACON_UNLOCK;
		if (ts->isClone)
		    continue;
		UBIK_ADDR_LOCK;
		code = DISK_GetVersion(ts->disk_rxcid, &ts->version);
		UBIK_ADDR_UNLOCK;
		if (code == 0) {
		    /* perhaps this is the best version */
		    if (vcmp(ts->version, bestDBVersion) > 0) {
			/* new best version */
			bestDBVersion = ts->version;
			bestServer = ts;
		    }
		}
	    }
	    /* take into consideration our version. Remember if we,
	     * the sync site, have the best version. Also note that
	     * we may need to send the best version out.
	     */
	    DBHOLD(ubik_dbase);
	    if (vcmp(ubik_dbase->version, bestDBVersion) >= 0) {
		bestDBVersion = ubik_dbase->version;
		bestServer = (struct ubik_server *)0;
		urecovery_state |= UBIK_RECHAVEDB;
	    } else {
		/* Clear the flag only when we know we have to retrieve
		 * the db. Because urecovery_AllBetter() looks at it.
		 */
		urecovery_state &= ~UBIK_RECHAVEDB;
	    }
	    urecovery_state |= UBIK_RECFOUNDDB;
	    urecovery_state &= ~UBIK_RECSENTDB;
	}
	if (!(urecovery_state & UBIK_RECFOUNDDB)) {
	    DBRELE(ubik_dbase);
	    continue;		/* not ready */
	}

	/* If we, the sync site, do not have the best db version, then
	 * go and get it from the server that does.
	 */
	if ((urecovery_state & UBIK_RECHAVEDB) || !bestServer) {
	    urecovery_state |= UBIK_RECHAVEDB;
	} else {
	    /* we don't have the best version; we should fetch it. */
	    urecovery_AbortAll(ubik_dbase);

	    /* Rx code to do the Bulk fetch */
	    file = 0;
	    offset = 0;
	    UBIK_ADDR_LOCK;
	    rxcall = rx_NewCall(bestServer->disk_rxcid);

	    ubik_print("Ubik: Synchronize database with server %s\n",
		       afs_inet_ntoa_r(bestServer->addr[0], hoststr));
	    UBIK_ADDR_UNLOCK;

	    code = StartDISK_GetFile(rxcall, file);
	    if (code) {
		ubik_dprint("StartDiskGetFile failed=%d\n", code);
		goto FetchEndCall;
	    }
	    nbytes = rx_Read(rxcall, (char *)&length, sizeof(afs_int32));
	    length = ntohl(length);
	    if (nbytes != sizeof(afs_int32)) {
		ubik_dprint("Rx-read length error=%d\n", code = BULK_ERROR);
		code = EIO;
		goto FetchEndCall;
	    }

	    /* give invalid label during file transit */
	    UBIK_VERSION_LOCK;
	    tversion.epoch = 0;
	    code = (*ubik_dbase->setlabel) (ubik_dbase, file, &tversion);
	    UBIK_VERSION_UNLOCK;
	    if (code) {
		ubik_dprint("setlabel io error=%d\n", code);
		goto FetchEndCall;
	    }
	    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
		     ubik_dbase->pathName, (file<0)?"SYS":"",
		     (file<0)?-file:file);
	    fd = open(pbuffer, O_CREAT | O_RDWR | O_TRUNC, 0600);
	    if (fd < 0) {
		code = errno;
		goto FetchEndCall;
	    }
	    code = lseek(fd, HDRSIZE, 0);
	    if (code != HDRSIZE) {
		close(fd);
		goto FetchEndCall;
	    }

	    pass = 0;
	    while (length > 0) {
		tlen = (length > sizeof(tbuffer) ? sizeof(tbuffer) : length);
#ifndef AFS_PTHREAD_ENV
		if (pass % 4 == 0)
		    IOMGR_Poll();
#endif
		nbytes = rx_Read(rxcall, tbuffer, tlen);
		if (nbytes != tlen) {
		    ubik_dprint("Rx-read bulk error=%d\n", code = BULK_ERROR);
		    code = EIO;
		    close(fd);
		    goto FetchEndCall;
		}
		nbytes = write(fd, tbuffer, tlen);
		pass++;
		if (nbytes != tlen) {
		    code = UIOERROR;
		    close(fd);
		    goto FetchEndCall;
		}
		offset += tlen;
		length -= tlen;
	    }
	    code = close(fd);
	    if (code)
		goto FetchEndCall;
	    code = EndDISK_GetFile(rxcall, &tversion);
	  FetchEndCall:
	    tcode = rx_EndCall(rxcall, code);
	    if (!code)
		code = tcode;
	    if (!code) {
		/* we got a new file, set up its header */
		urecovery_state |= UBIK_RECHAVEDB;
		UBIK_VERSION_LOCK;
		memcpy(&ubik_dbase->version, &tversion,
		       sizeof(struct ubik_version));
		snprintf(tbuffer, sizeof(tbuffer), "%s.DB%s%d",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
#ifdef AFS_NT40_ENV
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
		code = unlink(pbuffer);
		if (!code)
		    code = rename(tbuffer, pbuffer);
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
#endif
		if (!code)
		    code = rename(pbuffer, tbuffer);
		if (!code) {
		    (*ubik_dbase->open) (ubik_dbase, file);
		    /* after data is good, sync disk with correct label */
		    code =
			(*ubik_dbase->setlabel) (ubik_dbase, 0,
						 &ubik_dbase->version);
		}
		UBIK_VERSION_UNLOCK;
#ifdef AFS_NT40_ENV
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
		unlink(pbuffer);
#endif
	    }
	    if (code) {
		unlink(pbuffer);
		/*
		 * We will effectively invalidate the old data forever now.
		 * Unclear if we *should* but we do.
		 */
		UBIK_VERSION_LOCK;
		ubik_dbase->version.epoch = 0;
		ubik_dbase->version.counter = 0;
		UBIK_VERSION_UNLOCK;
		ubik_print("Ubik: Synchronize database failed (error = %d)\n",
			   code);
	    } else {
		ubik_print("Ubik: Synchronize database completed\n");
		urecovery_state |= UBIK_RECHAVEDB;
	    }
	    udisk_Invalidate(ubik_dbase, 0);	/* data has changed */
#ifdef AFS_PTHREAD_ENV
	    CV_BROADCAST(&ubik_dbase->version_cond);
#else
	    LWP_NoYieldSignal(&ubik_dbase->version);
#endif
	}
	if (!(urecovery_state & UBIK_RECHAVEDB)) {
	    DBRELE(ubik_dbase);
	    continue;		/* not ready */
	}

	/* If the database was newly initialized, then when we establish quorum, write
	 * a new label. This allows urecovery_AllBetter() to allow access for reads.
	 * Setting it to 2 also allows another site to come along with a newer
	 * database and overwrite this one.
	 */
	if (ubik_dbase->version.epoch == 1) {
	    urecovery_AbortAll(ubik_dbase);
	    UBIK_VERSION_LOCK;
	    version_globals.ubik_epochTime = 2;
	    ubik_dbase->version.epoch = version_globals.ubik_epochTime;
	    ubik_dbase->version.counter = 1;
	    code =
		(*ubik_dbase->setlabel) (ubik_dbase, 0, &ubik_dbase->version);
	    UBIK_VERSION_UNLOCK;
	    udisk_Invalidate(ubik_dbase, 0);	/* data may have changed */
#ifdef AFS_PTHREAD_ENV
	    CV_BROADCAST(&ubik_dbase->version_cond);
#else
	    LWP_NoYieldSignal(&ubik_dbase->version);
#endif
	}

	/* Check the other sites and send the database to them if they
	 * do not have the current db.
	 */
	if (!(urecovery_state & UBIK_RECSENTDB)) {
	    /* now propagate out new version to everyone else */
	    dbok = 1;		/* start off assuming they all worked */

	    /*
	     * Check if a write transaction is in progress. We can't send the
	     * db when a write is in progress here because the db would be
	     * obsolete as soon as it goes there. Also, ops after the begin
	     * trans would reach the recepient and wouldn't find a transaction
	     * pending there.  Frankly, I don't think it's possible to get past
	     * the write-lock above if there is a write transaction in progress,
	     * but then, it won't hurt to check, will it?
	     */
	    if (ubik_dbase->flags & DBWRITING) {
		struct timeval tv;
		int safety = 0;
		long cur_usec = 50000;
		while ((ubik_dbase->flags & DBWRITING) && (safety < 500)) {
		    DBRELE(ubik_dbase);
		    /* sleep for a little while */
		    tv.tv_sec = 0;
		    tv.tv_usec = cur_usec;
#ifdef AFS_PTHREAD_ENV
		    select(0, 0, 0, 0, &tv);
#else
		    IOMGR_Select(0, 0, 0, 0, &tv);
#endif
		    cur_usec += 10000;
		    safety++;
		    DBHOLD(ubik_dbase);
		}
	    }

	    for (ts = ubik_servers; ts; ts = ts->next) {
		UBIK_ADDR_LOCK;
		inAddr.s_addr = ts->addr[0];
		UBIK_ADDR_UNLOCK;
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    ubik_dprint("recovery cannot send version to %s\n",
				afs_inet_ntoa_r(inAddr.s_addr, hoststr));
		    dbok = 0;
		    continue;
		}
		UBIK_BEACON_UNLOCK;
		ubik_dprint("recovery sending version to %s\n",
			    afs_inet_ntoa_r(inAddr.s_addr, hoststr));
		if (vcmp(ts->version, ubik_dbase->version) != 0) {
		    ubik_dprint("recovery stating local database\n");

		    /* Rx code to do the Bulk Store */
		    code = (*ubik_dbase->stat) (ubik_dbase, 0, &ubikstat);
		    if (!code) {
			length = ubikstat.size;
			file = offset = 0;
			UBIK_ADDR_LOCK;
			rxcall = rx_NewCall(ts->disk_rxcid);
			UBIK_ADDR_UNLOCK;
			code =
			    StartDISK_SendFile(rxcall, file, length,
					       &ubik_dbase->version);
			if (code) {
			    ubik_dprint("StartDiskSendFile failed=%d\n",
					code);
			    goto StoreEndCall;
			}
			while (length > 0) {
			    tlen =
				(length >
				 sizeof(tbuffer) ? sizeof(tbuffer) : length);
			    nbytes =
				(*ubik_dbase->read) (ubik_dbase, file,
						     tbuffer, offset, tlen);
			    if (nbytes != tlen) {
				ubik_dprint("Local disk read error=%d\n",
					    code = UIOERROR);
				goto StoreEndCall;
			    }
			    nbytes = rx_Write(rxcall, tbuffer, tlen);
			    if (nbytes != tlen) {
				ubik_dprint("Rx-write bulk error=%d\n", code =
					    BULK_ERROR);
				goto StoreEndCall;
			    }
			    offset += tlen;
			    length -= tlen;
			}
			code = EndDISK_SendFile(rxcall);
		      StoreEndCall:
			code = rx_EndCall(rxcall, code);
		    }
		    if (code == 0) {
			/* we set a new file, process its header */
			ts->version = ubik_dbase->version;
			ts->currentDB = 1;
		    } else
			dbok = 0;
		} else {
		    /* mark file up to date */
		    ts->currentDB = 1;
		}
	    }
	    if (dbok)
		urecovery_state |= UBIK_RECSENTDB;
	}
	DBRELE(ubik_dbase);
    }
    return NULL;
}
Ejemplo n.º 2
0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flInterval - 
//			&m_LastMoveTarget - 
//			eMoveType - 
//-----------------------------------------------------------------------------
void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType )
{
	// dvs: something is setting this bit, causing us to stop moving and get stuck that way
	Forget( bits_MEMORY_TURNING );

	Vector Steer, SteerAvoid, SteerRel;
	Vector forward, right, up;

	//Get our orientation vectors.
	GetVectors( &forward, &right, &up);

	if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) )
	{
		SteerSeek( Steer, GetEnemy()->GetAbsOrigin() );
	}
	else
	{
		//If we are approaching our goal, use an arrival steering mechanism.
		if ( eMoveType == ICH_MOVETYPE_ARRIVE )
		{
			SteerArrive( Steer, MoveTarget );
		}
		else
		{
			//Otherwise use a seek steering mechanism.
			SteerSeek( Steer, MoveTarget );
		}
	}
	
#if FEELER_COLLISION

	Vector f, u, l, r, d;

	float	probeLength = GetAbsVelocity().Length();

	if ( probeLength < 150 )
		probeLength = 150;

	if ( probeLength > 500 )
		probeLength = 500;

	f = DoProbe( GetLocalOrigin() + (probeLength * forward) );
	r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) );
	l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) );
	u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) );
	d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) );

	SteerAvoid = f+r+l+u+d;
	
	//NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f );	

	if ( SteerAvoid.LengthSqr() )
	{
		Steer = (SteerAvoid*0.5f);
	}

	m_vecVelocity = m_vecVelocity + (Steer*0.5f);

	VectorNormalize( m_vecVelocity );

	SteerRel.x = forward.Dot( m_vecVelocity );
	SteerRel.y = right.Dot( m_vecVelocity );
	SteerRel.z = up.Dot( m_vecVelocity );

	m_vecVelocity *= m_flGroundSpeed;

#else

	//See if we need to avoid any obstacles.
	if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) )
	{
		//Take the avoidance vector
		Steer = SteerAvoid;
	}

	//Clamp our ideal steering vector to within our physical limitations.
	ClampSteer( Steer, SteerRel, forward, right, up );

	ApplyAbsVelocityImpulse( Steer * flInterval );
	
#endif

	Vector vecNewVelocity = GetAbsVelocity();
	float flLength = vecNewVelocity.Length();

	//Clamp our final speed
	if ( flLength > m_flGroundSpeed )
	{
		vecNewVelocity *= ( m_flGroundSpeed / flLength );
		flLength = m_flGroundSpeed;
	}

	Vector	workVelocity = vecNewVelocity;

	AddSwimNoise( &workVelocity );

	// Pose the fish properly
	SetPoses( SteerRel, flLength );

	//Drag our victim before moving
	if ( m_pVictim != NULL )
	{
		DragVictim( (workVelocity*flInterval).Length() );
	}

	//Move along the current velocity vector
	if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false )
	{
		//Attempt a half-step
		if ( WalkMove( (workVelocity*0.5f) * flInterval,  MASK_NPCSOLID) == false )
		{
			//Restart the velocity
			//VectorNormalize( m_vecVelocity );
			vecNewVelocity *= 0.5f;
		}
		else
		{
			//Cut our velocity in half
			vecNewVelocity *= 0.5f;
		}
	}

	SetAbsVelocity( vecNewVelocity );

}
Ejemplo n.º 3
0
void CIchthyosaur::Swim( )
{
	int retValue = 0;

	Vector start = pev->origin;

	Vector Angles;
	Vector Forward, Right, Up;

	if (FBitSet( pev->flags, FL_ONGROUND))
	{
		pev->angles.x = 0;
		pev->angles.y += RANDOM_FLOAT( -45, 45 );
		ClearBits( pev->flags, FL_ONGROUND );

		Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
		UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);

		pev->velocity = Forward * 200 + Up * 200;

		return;
	}

	if (m_bOnAttack && m_flightSpeed < m_flMaxSpeed)
	{
		m_flightSpeed += 40;
	}
	if (m_flightSpeed < 180)
	{
		if (m_IdealActivity == ACT_RUN)
			SetActivity( ACT_WALK );
		if (m_IdealActivity == ACT_WALK)
			pev->framerate = m_flightSpeed / 150.0;
		// ALERT( at_console, "walk %.2f\n", pev->framerate );
	}
	else
	{
		if (m_IdealActivity == ACT_WALK)
			SetActivity( ACT_RUN );
		if (m_IdealActivity == ACT_RUN)
			pev->framerate = m_flightSpeed / 150.0;
		// ALERT( at_console, "run  %.2f\n", pev->framerate );
	}

/*
	if (!m_pBeam)
	{
		m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 80 );
		m_pBeam->PointEntInit( pev->origin + m_SaveVelocity, entindex( ) );
		m_pBeam->SetEndAttachment( 1 );
		m_pBeam->SetColor( 255, 180, 96 );
		m_pBeam->SetBrightness( 192 );
	}
*/
#define PROBE_LENGTH 150
	Angles = UTIL_VecToAngles( m_SaveVelocity );
	Angles.x = -Angles.x;
	UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);

	Vector f, u, l, r, d;
	f = DoProbe(start + PROBE_LENGTH   * Forward);
	r = DoProbe(start + PROBE_LENGTH/3 * Forward+Right);
	l = DoProbe(start + PROBE_LENGTH/3 * Forward-Right);
	u = DoProbe(start + PROBE_LENGTH/3 * Forward+Up);
	d = DoProbe(start + PROBE_LENGTH/3 * Forward-Up);

	Vector SteeringVector = f+r+l+u+d;
	m_SaveVelocity = (m_SaveVelocity + SteeringVector/2).Normalize();

	Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
	UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
	// ALERT( at_console, "%f : %f\n", Angles.x, Forward.z );

	float flDot = DotProduct( Forward, m_SaveVelocity );
	if (flDot > 0.5)
		pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed;
	else if (flDot > 0)
		pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed * (flDot + 0.5);
	else
		pev->velocity = m_SaveVelocity = m_SaveVelocity * 80;

	// ALERT( at_console, "%.0f %.0f\n", m_flightSpeed, pev->velocity.Length() );


	// ALERT( at_console, "Steer %f %f %f\n", SteeringVector.x, SteeringVector.y, SteeringVector.z );

/*
	m_pBeam->SetStartPos( pev->origin + pev->velocity );
	m_pBeam->RelinkBeam( );
*/

	// ALERT( at_console, "speed %f\n", m_flightSpeed );
	
	Angles = UTIL_VecToAngles( m_SaveVelocity );

	// Smooth Pitch
	//
	if (Angles.x > 180)
		Angles.x = Angles.x - 360;
	pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 );
	if (pev->angles.x < -80) pev->angles.x = -80;
	if (pev->angles.x >  80) pev->angles.x =  80;

	// Smooth Yaw and generate Roll
	//
	float turn = 360;
	// ALERT( at_console, "Y %.0f %.0f\n", Angles.y, pev->angles.y );

	if (fabs(Angles.y - pev->angles.y) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y;
	}
	if (fabs(Angles.y - pev->angles.y + 360) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y + 360;
	}
	if (fabs(Angles.y - pev->angles.y - 360) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y - 360;
	}

	float speed = m_flightSpeed * 0.1;

	// ALERT( at_console, "speed %.0f %f\n", turn, speed );
	if (fabs(turn) > speed)
	{
		if (turn < 0.0)
		{
			turn = -speed;
		}
		else
		{
			turn = speed;
		}
	}
	pev->angles.y += turn;
	pev->angles.z -= turn;
	pev->angles.y = fmod((pev->angles.y + 360.0), 360.0);

	static float yaw_adj;

	yaw_adj = yaw_adj * 0.8 + turn;

	// ALERT( at_console, "yaw %f : %f\n", turn, yaw_adj );

	SetBoneController( 0, -yaw_adj / 4.0 );

	// Roll Smoothing
	//
	turn = 360;
	if (fabs(Angles.z - pev->angles.z) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z;
	}
	if (fabs(Angles.z - pev->angles.z + 360) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z + 360;
	}
	if (fabs(Angles.z - pev->angles.z - 360) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z - 360;
	}
	speed = m_flightSpeed/2 * 0.1;
	if (fabs(turn) < speed)
	{
		pev->angles.z += turn;
	}
	else
	{
		if (turn < 0.0)
		{
			pev->angles.z -= speed;
		}
		else
		{
			pev->angles.z += speed;
		}
	}
	if (pev->angles.z < -20) pev->angles.z = -20;
	if (pev->angles.z >  20) pev->angles.z =  20;

	UTIL_MakeVectorsPrivate( Vector( -Angles.x, Angles.y, Angles.z ), Forward, Right, Up);

	// UTIL_MoveToOrigin ( ENT(pev), pev->origin + Forward * speed, speed, MOVE_STRAFE );
}