Jump to content

[Guide]Πώς να φτιάξεις μία καλή μηχανή για Events.


Recommended Posts

Ο IronMan (aka) Tony Stark, δεν είχε πολύ μυαλό. Είχε πολύ καλά εργαλεία. Σε αυτό το topic θα σχεδιάσουμε μαζί μία μηχανή για Events.

 

Οι στόχοι του Topic:

 

1)Σχεδιασμός μηχανής για Event.

2)Καλή χρήση της Java.

3)Βήμα προς βήμα οδηγείες και για αυτούς που ξέρουν πολύ λίγη Java.

4)Σχεδιασμός ενός απλού Event.

 

Προαπαιτούμενα:

- Ο αναγνώστης γνωρίζει πώς να κάνει Compile με NetBeans ή Eclipse.

- O αναγνωστης γνωρίζει πώς να περνάει στο pack του Patches.

- O αvαγνωστης γνωρίζει τα πολύ βασικά για την Java.

- Χρησιμοποιούμε L2JServer epilogue ( αλλά και Interlude μας βολεύει. )

- Επισης μας βολευόυν τα δευτερέυοντα packs ( l2jbrazil, l2jteon, l2jarchid και λοιπές μαλακίες ).

 

Αν διαβάζεις αυτό το topic και δεν γνωρίζεις τα πρώτα τρία από παραπάνω φύγε από το topic, δεν θα καταλάβεις τίποτα. Απλά περίμενε να τελειώσουμε για να πάρεις το Patch έτοιμο στο τέλος (όπως άλλωστε συνηθίζεις εδώ και καιρό ...:) ). Δεν κάνω post την μηχανή για Events κατευθείαν παρόλο που την έχω έτοιμη γιατί στόχος του topic δεν είναι απλά να πετάξω ένα patch ξεκάρφωτο αλλά να δείξω πώς να γράφει κανείς σωστά κώδικα και να σχεδιάζει ( τουλάχιστον για τους 10 - 15 που ενδιαφέρονται ). Ξεκινάμε :).

 

Τα προβλήματα που έχουμε με τα Events αυτή τη στιγμή είναι οτι το καθένα ( tvt,dm,ctf ) πρέπει να το περάσεις σε 15 διαφορετικά μέρη του κώδικα. Επίσης με δεδομένο ένα event δεν είναι εύκολο να το τροποποιήσεις ώστε να φτιάξεις ένα event. Με βάση αυτές τις δύο παρατηρήσεις θα σχεδιάσουμε την μηχανή μας. Το πρώτο πράγμα που θα πρέπει να κάνουμε είναι να βρούμε τι κοινό έχουν τα περισσότερα events μεταξύ τους. Εγώ σκέυτηκα ώς εξής.

 

Κάθε event οτιδήποτε και να κάνει, περνάει από μία σειρά από στάδια. Για παράδειγμα:

 

-TVTEvent : Registration --> fighting --> rewarding --> end.

-CTFEvent: Registration --> fighting --> rewarding --> end.

 

Σαν πρώτη ιδέα λοιπον μπορούμε να πούμε οτι ένα event αποτελείτε από ένα σύνολο από στάδια. Με βάση αυτό ανακύπτει η ερώτηση: Πώς μεταβαίνουμε από στάδιο σε στάδιο ? Και η απάντηση θα μπορούσε να ήταν κάθε στάδιο εκτελεί κάποιες ενέργιες όταν ενεργοποιείτε, κάποιες ενέργειες όταν απενεργοποιείται και ενημερώνει το Event να μετακινηθεί στο επόμενo στάδιο. Ένα ολοκληρωμένο παράδειγμα είναι το TvTEvent όπως φαίνεται παρακάτω:

 

tvtevent.jpg

 

Αυτό που φαίνεται εξαρχής είναι οτι κάθε state κάνει κάποια πράγματα στην αρχή, κάποια στο τέλος και ενδιάμεσα ελέγχει συνθήκες. ΠΧ αν τελείωσε ο χρόνος του, άν δεν ικανοποιούνται οι συνθήκες και ενημερώνει το event λέγοντάς του σε ποιό state να μεταβεί. Για παράδειγμα άν το Registration State δεί οτι δέν υπάρχουν αρκετά άτομα για να γίνει το Event ενημερώνει το Event να λήξει. Αλλιώς του λέει να μεταβέι στο Fighting State.

 

To Be Continued.

Link to comment
Share on other sites

Κάθε event έχει μιά κατάσταση λειτουργίας και μιά κατάσταση Register. Για παράδειγμα ένα event είναι ενεργό ή ανενεργό. Επίσης ένα event έχει ενεργοποιημένη την εγγραφή ή απενεργοποιημένη ή δεν έχει καθόλου εγγραφή. Οι παρακάτω κώδικες αποτελόυν τμήμα του κάθε event και είναι Enumerations.

 

package L2Relapse.EventEngine.utils;

/**
* @author Issle
* 
* An event should be active or inactive. 
* All inner states of the event should not
* be in an enumeration, cuase the form a unique
* state class in the state machine model.
*
*/
public enum EventStatus
{
active, inactive
}

 

 

καί

 

package L2Relapse.EventEngine.utils;

/**
* @author Issle
*
*/
public enum RegistrationStatus
{
free,open,closed
}

 

Αυτά τα δύο ορίζουν πλήρως την κατάσταση ενός Event εξωτερικά, όπως την βλέπει ένας χρήστης που δεν συμετέχει στο Event.

Link to comment
Share on other sites

Ας πάμε τώρα στο Event State.

 

Κάθε Event State, έχει τα παρακάτω:

 

1)Έναν αριθμό που προσδιορίζει την θέση του ανάμεσα στα υπόλοιπα state του event.

2)Έναν αριθμό που προσδιορίζει ποιό state θα ακολουθήσει μετά την λήξη του.

3)Έναν αριθμό που προσδιορίζει για πόσο χρόνο θα διαρκέσει ένα State.

4)Αναφορά στο Event στο οποίο ανήκει, ας το ονομάσουμε AbstractEvent.

 

1)Μία συνάρτηση εισόδου (onEnterState()) που λέει τι θα γίνει μόλις το State ενεργοποιηθεί.

2)Μία συνάρτηση εξόδου ( onExitState()) που λέει τι θα γίνει μόλις το State τελειώσει.

3)Μία συνάρτηση ελέγχουν (checkConditions()) που εκτελείτε κάθε X seconds και ελέγχει την κατάσταση.

 

package L2Relapse.EventEngine;


/**
* @author Issle
*
*/
public abstract class AbstractEventState
{
protected int stateId;
protected int nextStateId;
protected int duration = 240000;
protected int remainingTime;
protected AbstractEvent activeEvent;

public void initializeTime()
{
	remainingTime = duration;
}

public AbstractEvent getEvent()
{
	return activeEvent;
}

public int getNextStateId()
{
	return nextStateId;
}

public void decreaseTime()
{
	remainingTime -= activeEvent.getClockRate();
}
public abstract void onEnterState();

public abstract void onExitState();

public abstract boolean checkConditions();
}

 

Η κλάση αυτή είναι Abstract που σημένει οτι μέρη της κλάσης δεν υλοποιούνται και ούτε μπορεί να χρησημοποιηθεί. Η κλάση αυτή ορίζει ένα πρότυπο για το πώς θα σχεδιαστούν τα States. Δηλαδή ορίζει για παράδειγμα οτι όλα τα States έχουν συνάρτηση onEnterState() αλλά καθένα ίσως την υλοποιήσει διαφορετικά.

Link to comment
Share on other sites

Όμοια και για το Event έχουμε ένα AbstractEvent που ορίζει το καλούπι για όλα τα Events.

Κάθε event έχει:

 

1)Ρολόι

2)Όνομα

3)Λίστα από στάδια ( px Registration , Fight ktl ktl ).

 

Επίσης το Event περιέχει συνθήκες ελέγχου onKill κτλ. Δεν υλοποιούνται γιατί όπως και προηγουμένος η κλάση αποτελεί καλούπι κατα κάποιο τρόπο. Επίσης έχει έναν χρονιστή ( που θέλει λίγη βελτίωση και Optimization  :-\ sorry ).

package L2Relapse.EventEngine;

import java.util.LinkedList;

import com.l2jserver.gameserver.ThreadPoolManager;
import L2Relapse.EventEngine.utils.EventStatus;
import L2Relapse.EventEngine.utils.RegistrationStatus;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;

/**
* @author Issle
* 
* Common information that all the events use.
*
*/
public abstract class AbstractEvent
{
public LinkedList<AbstractEventState> eventStates = new LinkedList<AbstractEventState>();

//Setting event parameters.
public int clockRate = 10000;
public String name = "NoName";

//The active state of this event.
public AbstractEventState activeState;

//The status of the event ( open /closed ).
public EventStatus eventStatus;

public RegistrationStatus registrationStatus;

/**
 * This is the core initialization of the event engine. It
 * reads the first state, and then schedules the checks for
 * the advancement of the event. The clock rate will not 
 * affect the slow/fast pace of the event. It controls the
 * rate in which the core checks for conditions in each event
 * state.
 */
public void startEvent()
{
	if(eventStates.isEmpty())
		return;

	//The event has started, change its status to active.
	eventStatus = EventStatus.active;

	//Get the activeState of the event whici is always the first state.
	activeState = eventStates.get(0);

	//Execute the activeState.
	activeState.onEnterState();

	//Check the advancing conditions on the future.
	ThreadPoolManager.getInstance().scheduleGeneral(new ConditionTask(this), clockRate);
}

/**
 * This class checks the active states conditions. If
 * the conditions are met, the state advances. Each state
 * indicates to what state the system must advance.
 * 
 * @author Issle
 *
 */
public class ConditionTask implements Runnable
{
	AbstractEvent event;

	public ConditionTask(AbstractEvent event)
	{
		this.event = event;
	}
	public void run()
	{
		//Are the conditions met ?
		if(activeState.checkConditions())
		{
			//Exits the current state.
			activeState.onExitState();

			//Tries to load the next state.
			if(activeState.getNextStateId()!= 0)
			{
				//Change the active state.
				activeState = eventStates.get(activeState.getNextStateId()-1);

				//Enter the current state.
				activeState.onEnterState();
			}
		}

		//If there is NO other state, then the event has ended.
		//In that case do not schedule a new conditions task.
		if(activeState.getNextStateId()!= 0)
		{
			ThreadPoolManager.getInstance().scheduleGeneral(new ConditionTask(event), clockRate);
			return;
		}

		//else ...
		TSManager.clearEvent();
		System.out.println("Event is now inactive.");
	}

}

public String getName()
{
	return name;
}

public void setEventStatus(EventStatus evt)
{
	eventStatus = evt;
}

/**
 * @param activeChar
 */
public abstract void register(L2PcInstance activeChar);

/**
 * @param activeChar
 */
public abstract void onEscapeUse(L2PcInstance activeChar);

/**
 * @param killerCharacter
 * @param killedPlayerInstance
 */
public abstract void onKill(L2Character killerCharacter, L2PcInstance killedPlayerInstance);

/**
 * @param playerInstance
 */
public abstract void onLogin(L2PcInstance playerInstance);

/**
 * @param playerInstance
 */
public abstract void onLogout(L2PcInstance playerInstance);

/**
 * Cleans the teams.
 */
public abstract void cleanTeams();

public void setRegistrationStatus(RegistrationStatus status) {
	registrationStatus = status;
}

/**
 * Returns the current events clock rate in milliseconds.
 * @return clockRate
 */
public int getClockRate() {
	return clockRate;
}
}

 

Πλέον έχουμε έτοιμο το καλούπι για τα state για τα Event και απομένεί να χρησιμοποιήσουμε μία κλάση που θα συνδέσει τα Event μας με το L2J.

 

To be continued ...

Link to comment
Share on other sites

Για να συνδέσουμε όλα τα Events με το l2j θα χρησιμοποιήσουμε μια κλαση διεπαφής με όνομα TSManager.

 

package L2Relapse.EventEngine;

import java.util.Calendar;
import java.util.logging.Logger;
import javolution.util.FastMap;
import L2Relapse.EventEngine.Conquest.ConquestEvent;
import L2Relapse.EventEngine.utils.EventStatus;
import L2Relapse.Utils.Developer;

import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;

/**
* @author Issle
*
*/
public class TSManager
{
protected static final Logger _log = Logger.getLogger(TSManager.class.getName());

//Indicates if the TSManager is running an event atm.
public static boolean eventIsRunning = false;

//Holds the active event that is running atm.
public static AbstractEvent activeEvent = null;

//A list of the events that the manager will run and their dates.
public FastMap<Calendar, AbstractEvent> events;

//Sample event for testing.
ConquestEvent sampleEvent = new ConquestEvent(5000, "Conquest Event");


public void loadEvents()
{
	//Initialize the events list.
	events = new FastMap<Calendar, AbstractEvent>();
	activeEvent = sampleEvent;
}

public synchronized static void setEvent()
{
	activeEvent.setEventStatus(EventStatus.active);
	eventIsRunning = true;
}

/**
 * Inactivates the active event and informs the Scheduling manager.
 */
public synchronized static void clearEvent()
{
	activeEvent.setEventStatus(EventStatus.inactive);
	eventIsRunning = false;
}

public synchronized static boolean getEvent()
{
	return eventIsRunning;
}

public class ManagerTask implements Runnable
{
	public ManagerTask()
	{
		//doNothing.
	}

	@Override
	public void run()
	{
		ThreadPoolManager.getInstance().scheduleGeneral(new ManagerTask(), 15000);

		if(getEvent())
		{
			Developer.printEventMessage("THere is an event running.");
			return;
		}

		setEvent();
		activeEvent.startEvent();
	}
}

public static void onLogin(L2PcInstance activeChar)
{
	if(eventIsRunning)
		activeEvent.onLogin(activeChar);
}
public static void onLogout(L2PcInstance activeChar)
{
	if(eventIsRunning)
		activeEvent.onLogout(activeChar);
}
public static void onKill(L2Character killerCharacter, L2PcInstance killedPlayerInstance)
{
	if(eventIsRunning)
		activeEvent.onKill(killerCharacter, killedPlayerInstance);
}
public static void onEscapeUse(L2PcInstance activeChar)
{
	if(eventIsRunning)
		activeEvent.onEscapeUse(activeChar);
}

public static boolean isInEvent(L2PcInstance activeChar)
{
	if(eventIsRunning && activeEvent != null)
	{
		return true;
	}
	return false;
}

private TSManager()
{
	_log.info("[L2Relapse]Starting Time Scheduling Manager.");
	loadEvents();
	ThreadPoolManager.getInstance().scheduleGeneral(new ManagerTask(), 15000);
}

public static class SingletonHolder{
	public static final TSManager INSTANCE = new TSManager();
}

public static TSManager getInstance(){
	return SingletonHolder.INSTANCE;
}
}

Link to comment
Share on other sites

Στο επόμενο post τώρα που η μηχανή είναι έτοιμη θα σχεδιάσουμε ένα Event στο οποίο οι πέκτες κάνουν PVP για ένα Raid Boss.

Link to comment
Share on other sites

Poli kalo file ama to testarei kaneis kai odos work tha pareis +1 karma

Tzampa tha paei to karma ;) Tha to ksanaxaso brizontas tous axristous mods kai devs. H mhxanh mexri tora einai etoimi alla den exei events pano opos den mporeis na testareis tipota :) Otan kano post to proto event tote tha mporeis na testareis eykola.

Link to comment
Share on other sites

  • 4 weeks later...
  • 3 months later...

Polu kalo tha to dokimaso aurio :) keep sharing.

lei den ine gia dokimi akomh otan ine tha to bali ara den mporis na testaris. pantos ksero elaxista pramata apo java exo gracia final l2jfree opote den me help emena pantos brabo sou
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disbaled AdBlock