Exemple #1
0
bool psCharAppearance::Detach(const char* socketName, bool removeItem )
{
    if (!socketName || !state.IsValid())
    {
        return false;
    }
    CS_ASSERT(state.IsValid());


    csRef<iSpriteCal3DSocket> socket = state->FindSocket( socketName );
    if ( !socket )
    {
        Notify2(LOG_CHARACTER, "Socket %s not found.", socketName );
        return false;
    }

    csRef<iMeshWrapper> meshWrap = socket->GetMeshWrapper();
    if ( !meshWrap )
    {
        Notify2(LOG_CHARACTER, "No mesh in socket: %s.", socketName );
    }
    else
    {
        meshWrap->QuerySceneNode()->SetParent (0);
        socket->SetMeshWrapper( NULL );
        if(removeItem)
            engine->RemoveObject( meshWrap );
    }

    usedSlots.Delete(socketName);
    return true;
}
Exemple #2
0
bool psCharAppearance::Attach(const char* socketName, const char* meshFactName, const char* materialName)
{
    if (!socketName || !meshFactName || !state.IsValid())
    {
        return false;
    }
    CS_ASSERT(state.IsValid());

    csRef<iSpriteCal3DSocket> socket = state->FindSocket( socketName );
    if ( !socket )
    {
        Notify2(LOG_CHARACTER, "Socket %s not found.", socketName);
        return false;
    }

    bool failed = false;
    csRef<iMeshFactoryWrapper> factory = psengine->GetLoader()->LoadFactory(meshFactName, &failed);
    if(failed)
    {
        Notify2(LOG_CHARACTER, "Mesh factory %s not found.", meshFactName );
        return false;
    }

    csRef<iMaterialWrapper> material;
    if(materialName != NULL)
    {
        psengine->GetLoader()->LoadMaterial(materialName, &failed);
        if(failed)
        {
            Notify2(LOG_CHARACTER, "Material %s not found.", materialName);
            return false;
        }
    }

    if(!factory.IsValid() || (materialName != NULL && !material.IsValid()))
    {
        Attachment attach(true);
        attach.factName = meshFactName;
        attach.materialName = materialName;
        attach.socket = socket;
        if(delayedAttach.IsEmpty())
        {
            psengine->RegisterDelayedLoader(this);
        }
        delayedAttach.PushBack(attach);
    }
    else
    {
        ProcessAttach(factory, material, meshFactName, socket);
    }

    return true;
}
void SpawnManager::RemoveNPC(gemObject *obj)
{
    Debug2(LOG_SPAWN,0,"RemoveNPC:%s\n",obj->GetName() );

    ServerStatus::mob_deathcount++;

    PID pid = obj->GetPID();

    Notify3(LOG_SPAWN, "Sending NPC %s disconnect msg to %zu clients.\n", ShowID(obj->GetEID()), obj->GetMulticastClients().GetSize());

    if (obj->GetCharacterData()==NULL)
    {
        Error2("Character data for npc character %s was not found! Entity stays dead.\n", ShowID(pid));
        return;
    }

    SpawnRule *respawn = NULL;
    int spawnruleid = obj->GetCharacterData()->NPC_GetSpawnRuleID();

    if (spawnruleid)
    {
        // Queue for respawn according to rules
        respawn = rules.Get(spawnruleid, NULL);
    }

    if (!respawn)
    {
        if (spawnruleid == 0) // spawnruleid 0 is for non-respawning NPCs
        {
            Notify2(LOG_SPAWN,"Temporary NPC based on player ID %s has died. Entity stays dead.\n", ShowID(pid));
        }
        else
        {
            Error3("Respawn rule for player %s, rule %d was not found! Entity stays dead.\n", ShowID(pid), spawnruleid);
        }

        // Remove mesh, etc from engine
        entityManager->RemoveActor(obj);
        return;
    }

    // Remove mesh, etc from engine
    entityManager->RemoveActor(obj);

    int delay = respawn->GetRespawnDelay();
    PID newplayer = respawn->CheckSubstitution(pid);

    psRespawnGameEvent *newevent = new psRespawnGameEvent(this, delay, newplayer, respawn);
    psserver->GetEventManager()->Push(newevent);

    Notify3(LOG_SPAWN, "Scheduled NPC %s to be respawned in %.1f seconds", ShowID(newplayer), (float)delay/1000.0);
}
Exemple #4
0
NetBase::~NetBase()
{
    if (ready)
        Close();

    Notify2(LOG_ANY,"Total bytes sent out was %li.\n",totaltransferout);
    Notify2(LOG_ANY,"Total bytes received was %li.\n", totaltransferin);

    socklibrefcount--;
    if (socklibrefcount==0)
    {
        exitSocket();
    }

    if (randomgen)
        delete randomgen;
    
    delete profs;

    if (input_buffer)
        cs_free(input_buffer);
}
Exemple #5
0
bool AuthenticationServer::CheckAuthenticationPreCondition(int clientnum, bool netversionok, const char * sUser)
{
    /**
     * CHECK 1: Is Network protokol compatible?
     */
    if (!netversionok)
    {
        psserver->RemovePlayer (clientnum, "You are not running the correct version of PlaneShift. Please launch the updater.");
        return false;
    }

    /**
     * CHECK 2: Server has loaded a map
     */

    // check if server is already ready (ie level loaded)
    if (!psserver->IsReady())
    {
        if (psserver->HasBeenReady())
        {
            // Locked
            psserver->RemovePlayer(clientnum,"The server is up but about to shutdown. Please try again in 2 minutes.");

            Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: Server does not accept connections anymore.", sUser );
        }
        else
        {
            // Not ready yet
            psserver->RemovePlayer(clientnum,"The server is up but not fully ready to go yet. Please try again in a few minutes.");

            Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: Server has not loaded a world map.\n", sUser );
        }
        
        return false;
    }

    return true;
}
Exemple #6
0
void WeatherManager::QueueNextEvent(int delayticks,
                                    int eventtype,
                                    int eventvalue,
                                    int duration,
                                    int fade,
                                    const char* sector,
                                    psSectorInfo* si,
                                    uint clientnum,
                                    int r,int g,int b)
{
    psWeatherGameEvent* event;

    // There are no notify for 7 pars so we create a string first.
    csString note;
    note.Format("QueueNextEvent Delay: %d Type: %d Value: %d "
                "Duration: %d Fade: %d Sector: %s ...",
                delayticks,eventtype,eventvalue,duration,
                fade,sector);
    Notify2(LOG_WEATHER, "%s", note.GetDataSafe());

    event = new psWeatherGameEvent(this,
                                   delayticks,
                                   eventtype,
                                   eventvalue,
                                   duration,
                                   fade,
                                   sector,
                                   si,
                                   clientnum,
                                   r,g,b);
    {
        CS::Threading::MutexScopedLock lock(eventsMutex);
        events.Push(event);
    }
    psserver->GetEventManager()->Push(event);
}
Exemple #7
0
void AuthenticationServer::HandleAuthent(MsgEntry *me, Client *notused)
{
    csTicks start = csGetTicks();

    psAuthenticationMessage msg(me); // This cracks message into members.

    if (!msg.valid)
    {
        Debug1(LOG_NET,me->clientnum,"Mangled psAuthenticationMessage received.\n");
        return;
    }

    if (!CheckAuthenticationPreCondition(me->clientnum, msg.NetVersionOk(),msg.sUser))
        return;

    csString status;
    status.Format("%s, %u, Received Authentication Message", (const char *) msg.sUser, me->clientnum);
    psserver->GetLogCSV()->Write(CSV_AUTHENT, status);

    if ( msg.sUser.Length() == 0 || msg.sPassword.Length() == 0)
    {
        psserver->RemovePlayer(me->clientnum,"No username or password entered");

        Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: No username or password.\n",
                (const char *)msg.sUser);            
        return;                
    }
    
    // Check if login was correct
    Notify2(LOG_CONNECTIONS,"Check Login for: '%s'\n", (const char*)msg.sUser);
    psAccountInfo *acctinfo=CacheManager::GetSingleton().GetAccountInfoByUsername((const char *)msg.sUser);

    if ( !acctinfo )
    {
        // invalid
        psserver->RemovePlayer(me->clientnum,"Incorrect password or username.");

        Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: No account found with that name.\n",
                (const char *)msg.sUser);            
        return;                
    }

    // Add account to cache to optimize repeated login attempts
    CacheManager::GetSingleton().AddToCache(acctinfo,msg.sUser,120);
    
    // Check if password was correct
    csString passwordhashandclientnum (acctinfo->password);
    passwordhashandclientnum.Append(":");
    passwordhashandclientnum.Append(me->clientnum);
    
    csString encoded_hash = csMD5::Encode(passwordhashandclientnum).HexString();
    if (strcmp( encoded_hash.GetData() , msg.sPassword.GetData())) // authentication error
    {
        psserver->RemovePlayer(me->clientnum, "Incorrect password or username.");
        Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected (Bad password).",(const char *)msg.sUser);
        // No delete necessary because AddToCache will auto-delete
        // delete acctinfo;
        return;
    }
    
    /**
     * Check if the client is already logged in
     */
    Client* existingClient = clients->FindAccount(acctinfo->accountid, me->clientnum);
    if (existingClient)  // account already logged in
    {
        // invalid authent message from a different client
        csString reason;
        if(existingClient->IsZombie())
        {
            reason.Format("Your character(%s) was still in combat or casting a spell when you disconnected. "
                          "This connection is being overridden by a new login.", existingClient->GetName());
        }
        else
        {
            reason.Format("You are already logged on to this server as %s. "
                          "This connection is being overridden by a new login..", existingClient->GetName());
        }

        psserver->RemovePlayer(existingClient->GetClientNum(), reason);
        Notify2(LOG_CONNECTIONS,"User '%s' authentication request overrides an existing logged in user.\n",
            (const char *)msg.sUser);

        // No delete necessary because AddToCache will auto-delete
        // delete acctinfo;
    }


    if(csGetTicks() - start > 500)
    {
        csString status;
        status.Format("Warning: Spent %u time authenticating account ID %u, After password check", 
            csGetTicks() - start, acctinfo->accountid);
        psserver->GetLogCSV()->Write(CSV_STATUS, status);
    }


    Client *client = clients->FindAny(me->clientnum);
    if (!client)
    {
        Bug2("Couldn't find client %d?!?",me->clientnum);
        // No delete necessary because AddToCache will auto-delete
        // delete acctinfo;
        return;
    }

    client->SetName(msg.sUser);
    client->SetAccountID( acctinfo->accountid );
    

    // Check to see if the client is banned
    time_t now = time(0);
    BanEntry* ban = banmanager.GetBanByAccount(acctinfo->accountid);
    if (ban == NULL)
    {
        // Account not banned; try IP range
        ban = banmanager.GetBanByIPRange(client->GetIPRange());
        // 2 day IP ban limit removed
        //if (ban && ban->end && now > ban->start + IP_RANGE_BAN_TIME)
        //{  
        //    // Only ban by IP range for the first 2 days
        //    ban = NULL;
        //}
    }
    if (ban)
    {
        if (now > ban->end)  // Time served
        {
            banmanager.RemoveBan(acctinfo->accountid);
        }
        else  // Notify and block
        {
            tm* timeinfo = gmtime(&(ban->end));
            csString banmsg;
            banmsg.Format("You are banned until %d-%d-%d %d:%d GMT.  Reason: %s",
                          timeinfo->tm_year+1900,
                          timeinfo->tm_mon+1,
                          timeinfo->tm_mday,
                          timeinfo->tm_hour,
                          timeinfo->tm_min,
                          ban->reason.GetData() );
    
            psserver->RemovePlayer(me->clientnum, banmsg);
    
            Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected (Banned).",(const char *)msg.sUser);
            // No delete necessary because AddToCache will auto-delete
            // delete acctinfo;
            return;
        }
    }

    if(csGetTicks() - start > 500)
    {
        csString status;
        status.Format("Warning: Spent %u time authenticating account ID %u, After ban check", 
            csGetTicks() - start, acctinfo->accountid);
        psserver->GetLogCSV()->Write(CSV_STATUS, status);
    }

    /** Check to see if there are any players on that account.  All accounts should have
    *    at least one player in this function.
    */
    psCharacterList *charlist = psserver->CharacterLoader.LoadCharacterList(acctinfo->accountid);

    if (!charlist)
    {
        Error2("Could not load Character List for account! Rejecting client %s!\n",(const char *)msg.sUser);
        psserver->RemovePlayer( me->clientnum, "Could not load the list of characters for your account.  Please contact a PS Admin for help.");
        delete acctinfo;
        return;
    }

    // cache will auto-delete this ptr if it times out
    CacheManager::GetSingleton().AddToCache(charlist, CacheManager::GetSingleton().MakeCacheName("list", client->GetAccountID().Unbox()),120);

    
     /**
     * CHECK 6: Connection limit
     * 
     * We check against number of concurrent connections, but players with
     * security rank of GameMaster or higher are not subject to this limit.
     */
    if (psserver->IsFull(clients->Count(),client)) 
    {
        // invalid
        psserver->RemovePlayer(me->clientnum, "The server is full right now.  Please try again in a few minutes.");

        Notify2(LOG_CONNECTIONS, "User '%s' authentication request rejected: Too many connections.\n", (const char *)msg.sUser );
        // No delete necessary because AddToCache will auto-delete
        // delete acctinfo;
        status = "User limit hit!";
        psserver->GetLogCSV()->Write(CSV_STATUS, status);
        return;
    }

    Notify3(LOG_CONNECTIONS,"User '%s' (%d) added to active client list\n",(const char*) msg.sUser, me->clientnum);

    // Get the struct to refresh
    // Update last login ip and time
    char addr[20];
    client->GetIPAddress(addr);
    acctinfo->lastloginip = addr;

    tm* gmtm = gmtime(&now);
    csString timeStr;
    timeStr.Format("%d-%02d-%02d %02d:%02d:%02d",
        gmtm->tm_year+1900,
        gmtm->tm_mon+1,
        gmtm->tm_mday,
        gmtm->tm_hour,
        gmtm->tm_min,
        gmtm->tm_sec);

    acctinfo->lastlogintime = timeStr;
    acctinfo->os = msg.os_;
    acctinfo->gfxcard = msg.gfxcard_;
    acctinfo->gfxversion = msg.gfxversion_;
    CacheManager::GetSingleton().UpdateAccountInfo(acctinfo);

    iCachedObject *obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("auth",acctinfo->accountid));
    CachedAuthMessage *cam;

    if (!obj)
    {
        // Send approval message
        psAuthApprovedMessage *message = new psAuthApprovedMessage(me->clientnum,client->GetPID(), charlist->GetValidCount() );    

        if(csGetTicks() - start > 500)
        {
            csString status;
            status.Format("Warning: Spent %u time authenticating account ID %u, After approval", 
                csGetTicks() - start, acctinfo->accountid);
            psserver->GetLogCSV()->Write(CSV_STATUS, status);
        }

        // Send out the character list to the auth'd player    
        for (int i=0; i<MAX_CHARACTERS_IN_LIST; i++)
        {
            if (charlist->GetEntryValid(i))
            {
                // Quick load the characters to get enough info to send to the client
                psCharacter* character = psserver->CharacterLoader.QuickLoadCharacterData( charlist->GetCharacterID(i), false );
                if (character == NULL)
                {
                    Error2("QuickLoadCharacterData failed for character '%s'", charlist->GetCharacterName(i));
                    continue;
                }

                Notify3(LOG_CHARACTER, "Sending %s to client %d\n", character->name.GetData(), me->clientnum );
                character->AppendCharacterSelectData(*message);

                delete character;
            }
        }
        message->ConstructMsg();
        cam = new CachedAuthMessage(message);
    }
    else
    {
        // recover underlying object
        cam = (CachedAuthMessage *)obj->RecoverObject();
        // update client id since new connection here
        cam->msg->msg->clientnum = me->clientnum;
    }
    // Send auth approved and char list in one message now
    cam->msg->SendMessage();
    CacheManager::GetSingleton().AddToCache(cam, CacheManager::GetSingleton().MakeCacheName("auth",acctinfo->accountid), 10);

    SendMsgStrings(me->clientnum, true); 
    
    client->SetSpamPoints(acctinfo->spamPoints);
    client->SetAdvisorPoints(acctinfo->advisorPoints);
    client->SetSecurityLevel(acctinfo->securitylevel);

    if (acctinfo->securitylevel >= GM_TESTER)
    {
        psserver->GetAdminManager()->Admin(me->clientnum, client);
    }
    
    if (CacheManager::GetSingletonPtr()->GetCommandManager()->Validate(client->GetSecurityLevel(), "default advisor"))
        psserver->GetAdviceManager()->AddAdvisor(client);

    if (CacheManager::GetSingletonPtr()->GetCommandManager()->Validate(client->GetSecurityLevel(), "default buddylisthide"))
        client->SetBuddyListHide(true);

    psserver->GetWeatherManager()->SendClientGameTime(me->clientnum);

    if(csGetTicks() - start > 500)
    {
        csString status;
        status.Format("Warning: Spent %u time authenticating account ID %u, After load", 
            csGetTicks() - start, acctinfo->accountid);
        psserver->GetLogCSV()->Write(CSV_STATUS, status);
    }

    status.Format("%s - %s, %u, Logged in", addr, (const char*) msg.sUser, me->clientnum);
    psserver->GetLogCSV()->Write(CSV_AUTHENT, status);
}
Exemple #8
0
void WeatherManager::HandleWeatherEvent(psWeatherGameEvent* event)
{
    {
        CS::Threading::MutexScopedLock lock(eventsMutex);
        events.Delete(event); // Delete this from our "db"
    }

    // See if we want to ignore this event
    for(size_t i = 0; i < ignored.GetSize(); i++)
    {
        if(event == ignored[i])
        {
            ignored.DeleteIndex(i);
            return;
        }
    }

    switch(event->type)
    {
    case psWeatherMessage::SNOW:
    case psWeatherMessage::RAIN:
    {
        event->si->current_rain_drops = event->value;

        Notify4(LOG_WEATHER,"New %s in sector '%s': %d",event->type == psWeatherMessage::SNOW ? "snow" : "rain",  event->si->name.GetData(),event->value);

        psWeatherMessage::NetWeatherInfo info;

        info.has_downfall     = true;
        info.downfall_is_snow = (event->type == psWeatherMessage::SNOW);
        info.has_fog          = true;
        info.has_lightning    = false;

        info.downfall_drops = event->value;

        // Save current fog and calculate new.
        if(event->value)
        {
            // Only save the fog 'history' once. After that we just override.
            if(!event->si->densitySaved)
            {
                event->si->fog_density_old = event->si->fog_density;
                event->si->densitySaved = true;
            }

            // Set fog colour if there's not already fog.
            if(!event->si->fog_density)
            {
                event->si->r = 255;
                event->si->g = 255;
                event->si->b = 255;
            }

            if(info.downfall_drops < 8000)
            {
                // Calculate fog to be linear in range 0 to 200
                event->si->fog_density =
                    (int)(200.0f*(info.downfall_drops-1000.0f)/8000.0f);
            }
            else
            {
                event->si->fog_density = 200;
            }
        }
        else if(event->si->fog_density)
        {
            // Restore fog, if the fog wasn't turned off.
            event->si->fog_density = event->si->fog_density_old;
            event->si->densitySaved = false;
        }

        info.fog_density = event->si->fog_density;

        info.r = event->si->r;
        info.g = event->si->g;
        info.b = event->si->b;

        info.sector = event->sector;

        if(event->fade)
        {
            info.downfall_fade = event->fade;
            info.fog_fade = event->fade;
        }
        else
        {
            if(event->value)
            {
                info.downfall_fade = event->si->GetRandomWeatherFadeIn((unsigned int)event->type);
                info.fog_fade = info.downfall_fade;
            }
            else
            {
                info.downfall_fade = event->si->GetRandomWeatherFadeOut((unsigned int)event->type);
                info.fog_fade = info.downfall_fade;
            }
        }

        Notify4(LOG_WEATHER,"Drops: %d Density: %d Sector: %s\n",
                info.downfall_drops,info.fog_density,info.sector.GetDataSafe());

        psWeatherMessage rain(0,info);
        if(rain.valid)
            psserver->GetEventManager()->Broadcast(rain.msg,NetBase::BC_EVERYONE);
        else
        {
            Bug1("Could not create valid psWeatherMessage (rain) for broadcast.\n");
        }

        // Make sure we don't have any other events in this sector that will disturb
        // Simple case is when event types are equal. In addition we have to test
        // for the mutal exclusive case where we are changing from snow to rain or
        // rain to snow.

        {
            CS::Threading::MutexScopedLock lock(eventsMutex);
            for(size_t i = 0; i < events.GetSize(); i++)
            {
                psWeatherGameEvent* evt = events[i];
                if(evt->sector == event->sector && (evt->type == event->type
                                                    || ((evt->type == psWeatherMessage::RAIN || evt->type
                                                            == psWeatherMessage::SNOW) && (event->type
                                                                    == psWeatherMessage::RAIN || event->type
                                                                    == psWeatherMessage::SNOW))))
                {
                    ignored.Push(evt); // Ignore when the eventmanager handles the event
                    events.DeleteIndex(i);
                    i--;

                    Notify4(LOG_WEATHER,"Removed disturbing event for sector '%s' (%d,%d)",
                            evt->sector.GetData(),evt->value,evt->duration);

                }
            }
        }

        if(event->value)   // Queue event to turn off rain/snow.
        {
            if(event->type != psWeatherMessage::SNOW)
                event->si->is_raining = true;
            else
                event->si->is_snowing = true;

            if(event->si->GetWeatherEnabled((unsigned int) psWeatherMessage::LIGHTNING) &&
                    event->value > 2000 && event->si->is_raining)
            {
                // Queue lightning during rain storm here first
                QueueNextEvent(event->si->GetRandomWeatherGap((unsigned int) psWeatherMessage::LIGHTNING),
                               psWeatherMessage::LIGHTNING,
                               0,
                               0,
                               0,
                               event->si->name,
                               event->si,
                               event->clientnum);
            }

            // Queue event to stop rain/snow
            int duration;
            if(event->duration != -1)
            {
                if(event->duration)
                {
                    duration = event->duration;
                }
                else
                {
                    duration = event->si->GetRandomWeatherDuration((unsigned int) event->type);
                }

                QueueNextEvent(duration,
                               event->type,
                               0,
                               0,
                               event->fade,
                               event->si->name,
                               event->si);
            }

        }
        else // Stop rain/snow.
        {
            if(event->type== psWeatherMessage::SNOW)
                event->si->is_snowing = false;
            else
                event->si->is_raining = false;

            // Queue event to turn on again later if enabled
            StartWeather(event->si);
        }

        break;
    }
    case psWeatherMessage::FOG:
    {
        // Update sector weather info
        event->si->fog_density = event->value;
        event->si->r = event->cr;
        event->si->g = event->cg;
        event->si->b = event->cb;

        // Update the clients
        psWeatherMessage::NetWeatherInfo info;
        info.has_downfall     = false;
        info.downfall_is_snow = false;
        info.has_fog          = true;
        info.has_lightning    = false;
        info.sector           = event->si->name;
        info.fog_density      = event->si->fog_density;
        info.r                = event->cr;
        info.g                = event->cg;
        info.b                = event->cb;
        info.downfall_drops   = 0;
        info.downfall_fade    = 0;

        Notify3(LOG_WEATHER,"New Fog in sector '%s': %d",  event->si->name.GetData(), event->value);

        // Save the fade in so we can reverse it when fading out.
        if(event->fade)
        {
            event->si->fogFade = event->fade;
        }
        else
        {
            // We're removing fog, so removed the 'saved' flag.
            event->si->densitySaved = false;
        }
        info.fog_fade = event->si->fogFade;

        psWeatherMessage fog(0,info);
        if(fog.valid)
            psserver->GetEventManager()->Broadcast(fog.msg,NetBase::BC_EVERYONE);
        else
        {
            Bug1("Could not create valid psWeatherMessage (fog) for broadcast.\n");
        }

        if(event->value)   // Queue event to turn off fog.
        {

            // Queue event to stop rain/snow
            int duration;
            if(event->duration != -1)
            {
                if(event->duration)
                {
                    duration = event->duration;
                }
                else
                {
                    duration = event->si->GetRandomWeatherDuration((unsigned int) event->type);
                }

                QueueNextEvent(duration,
                               event->type,
                               0,
                               0,
                               event->si->GetRandomWeatherFadeOut((unsigned int)psWeatherMessage::FOG),
                               event->si->name,
                               event->si);
            }

        }
        else // Stop fog.
        {
            // Queue event to turn on again later if enabled
            StartWeather(event->si, psWeatherMessage::FOG);
        }

        break;
    }
    case psWeatherMessage::LIGHTNING:
    {
        if(event->si->is_raining)
        {
            Notify2(LOG_WEATHER,"Lightning in sector '%s'",event->sector.GetData());

            psWeatherMessage::NetWeatherInfo info;
            info.has_downfall  = false;
            info.downfall_is_snow = false;
            info.has_fog       = false;
            info.has_lightning = true;
            info.sector = event->sector;
            info.fog_fade = info.downfall_fade = 0;
            info.r = info.g = info.b = 0;
            info.downfall_drops = info.fog_density = 0;

            psWeatherMessage lightning(0,info, event->clientnum);

            if(lightning.valid)
            {
                psserver->GetEventManager()->Broadcast(lightning.msg);
            }
            else
            {
                Bug1("Could not create valid psWeatherMessage (lightning) for broadcast.\n");
            }

            if(event->si->is_raining &&
                    event->si->GetWeatherEnabled((unsigned int) psWeatherMessage::LIGHTNING))
            {
                QueueNextEvent(event->si->GetRandomWeatherGap((unsigned int) psWeatherMessage::LIGHTNING),
                               psWeatherMessage::LIGHTNING,
                               0,
                               0,
                               0,
                               event->si->name,
                               event->si,
                               event->clientnum);
            }
        }
        break;
    }
    case psWeatherMessage::DAYNIGHT:
    {
        QueueNextEvent(GAME_MINUTE_IN_TICKS,
                       psWeatherMessage::DAYNIGHT,
                       0,
                       0,
                       0,
                       NULL,
                       NULL);

        gameTimeMinute++;
        if(gameTimeMinute >= 60)
        {
            gameTimeMinute = 0;
            gameTimeHour++;
            if(gameTimeHour >= 24)
            {
                gameTimeHour = 0;
                gameTimeDay++;
                if(gameTimeDay >= monthLengths[gameTimeMonth-1]+1)
                {
                    gameTimeDay = 1;
                    gameTimeMonth++;
                    if(gameTimeMonth >= MONTH_COUNT+1)
                    {
                        gameTimeMonth = 1;
                        gameTimeYear++;
                    }
                }

            }
            // Only save and broadcast every game hour.
            SaveGameTime();
            BroadcastGameTime();
        }
        else
        {
            // Super clients should get the time every minute
            BroadcastGameTimeSuperclients();
        }

        break;
    }
    default:
    {
        break;
    }
    }
}