Small knowledge, big challenge! This article is part of the “Programmer’s Essentials

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

Activity system is a subsystem that every game has. The unfixed content and the expression of different game base systems also bring some fresh elements to the game. Even a skin activity of King of Glory can earn 100 million yuan.

The campaign is designed to: increase monetization, retention, player activity, online time, LTV, etc.

Although the king of Glory code can not see, but today I take you to reproduce a king of Glory activity system, sit ready, ready to start, go

1. Activity type

Activities are also the most important way and means of earning revenue, which is also the main work of operating students. The most common operating activities are as follows:

1. Recharge activities, such as first recharge activities, recharge and send props and other activities

2. Rotary raffle activities, such as collecting fragments for raffle, or buying props for raffle;

3, open service activities; 7 days landing activities, open service

4. Regression activities; Invite old players back

5, blunt level activities, how many levels can receive gift packages, gift boxes.

6. Discount, limited time, group purchase promotion activities in the mall;

7. Daily and accumulated check-in activities;

8. BOSS activities; World Boss event, Guild Boss event

9. Competition activities; Cooking skills, etc

10. Online rewards and BUFF activities;

11, association activities, played before shumen have association tree to increase experience activities

12. Answer activities, answer activities of Ninja mobile games

13. Sharing activities; Share on moments to get rewards

2, requirements,

It can be seen from the first part that the needs of activities are various and the most important needs of the activity system

1. You can dynamically adjust online activities

2. Enable, disable, and claim the prize according to the configured time.

3. Convenient configuration, choose JSON format configuration, before the company used XML, very annoying, then there were hierarchical restrictions.

The demand is there, now we start to formulate the plan, also do not beat around the bush, directly clarify the technical plan we currently use.

1. Run configuration activities and publish them to the Web server

2. The operation invokes the Web command to notify each server to update the activity and read the new activity

3. The game server downloads the packaged activity data to the local

4. Read active data

5. Load it to the memory

3. File download

There are several common ways to download Java over HTTP,

One is to use httpClient,

The other is through HttpURLConnection to achieve, HttpURLConnection is JAVA standard class, is a native JAVA implementation.

I also chose to use Okhttp because I didn’t want to use HttpClient, simple as that.

Add the following dependencies to pom.xml:

   <dependency>
     <groupId>com.squareup.okhttp3</groupId>
     <artifactId>okhttp</artifactId>
     <version>4.8.0</version>
   </dependency>
Copy the code

To test netbox2.exe, you can write a Hello world index.html file and put it together with netbox2.exe. Then go directly to http://localhost:49983 (the port can be viewed by right clicking on the taskbar netbox and the default is port 80) and access index.html by default.

Advantages: this server does not need to install, direct operation can, at the same time relatively small, only less than 636K, easy to carry. Do not need to do any configuration, direct use, is a good choice when testing, online when you can switch to Tomcat or Nginx and other servers, want to this server can pay attention to my public number [cilancilanet chat game], reply NetBox can be.

The asynchronous download mechanism is used so that the thread is not blocked and the progress is called back.

The specific test code is as follows:

package com.ploy;
 
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
 
/** * file download tool class (singleton mode) **@authorParsley * /
@Slf4j
public class DownloadUtil {
   public static OkHttpClient okHttpClient;
 
   /** * Initializes httpClient **@return* /
   public static synchronized OkHttpClient getHttpClient(a) {
       if (okHttpClient == null) {
           okHttpClient = new OkHttpClient();
      }
       return okHttpClient;
  }
 
   public interface OnDownloadListener {
 
       /** * Download the file successfully */
       void onDownloadSuccess(File file);
 
       /** * Download progress */
       void onDownloading(int progress);
 
       /** * Failed message */
 
       void onDownloadFailed(Exception e);
  }
 
   / * * *@paramUrl download link *@paramDestFileDir Directory for storing downloaded files *@paramDestFileName Indicates the name of the downloaded file. Remember to add a suffix to the end of the file. Otherwise, the mobile phone cannot identify the file type *@paramListener Download listener */
   public static void downloadByAsync(final String url, final String destFileDir, final String destFileName, final OnDownloadListener listener) {
       Request request = new Request.Builder()
              .url(url)
              .build();
       // Asynchronous request
       getHttpClient().newCall(request).enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
               // Download failed to listen for callback
               listener.onDownloadFailed(e);
          }
 
           @Override
           public void onResponse(Call call, Response response) {
               if (response.body() == null) {
                   listener.onDownloadFailed(new Exception(" body is null"));
                   return;
              }
               byte[] buf = new byte[4096];
               int len = 0;
               // The directory where the downloaded files are stored
               File dir = new File(destFileDir);
               if(! dir.exists()) { dir.mkdirs(); } File file =new File(dir, destFileName);
 
               try (InputStream is = response.body().byteStream();
                    FileOutputStream fos = new FileOutputStream(file)) {
                   int size = 0;
                   long total = response.body().contentLength();
                   while((size = is.read(buf)) ! = -1) {
                       len += size;
                       fos.write(buf, 0, size);
                       int process = (int) Math.floor(((double) len / total) * 100);
                       // The console prints the percentage of files downloaded
                       listener.onDownloading(process);
                  }
                   fos.flush();
                   // Download complete
                   listener.onDownloadSuccess(file);
              } catch (Exception e) {
                   log.error("error:{}", e); listener.onDownloadFailed(e); }}}); }public static void main(String[] args) {
       // Asynchronous download
       DownloadUtil.downloadByAsync("http://localhost/aaa.zip"."E:\\video\\Learn\\Learn\\src\\main\\java\\com\\ploy\\"."abc.zip".new DownloadUtil.OnDownloadListener() {
                   @Override
                   public void onDownloadSuccess(File file) {
                       log.info("Download completed");
                  }
 
                   @Override
                   public void onDownloading(int progress) {
                       log.info("Downloading in progress" + progress);
                  }
 
                   @Override
                   public void onDownloadFailed(Exception e) {
                       // Perform related operations when the download is abnormal
                       log.error("Downloading error", e); }}); }Copy the code

4. Decompress files

After the file download back, need to decompress, decompression of the JDK to choose its own way to decompress, specific usage are in the code, it is not difficult.

Pathfile.mkdirs ();

There is also the use of zipfiles

package com.ploy; import java.io.*; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * UnzipUtil public class UnzipUtil {** ** UnzipUtil to the specified directory */ @suppressWarnings ("rawtypes") public static void  unZipFiles(File zipFile, String descDir) throws IOException { File pathFile = new File(descDir); if (! Pathfile.exists ()) {// Create pathfile.mkdirs () if the folder does not exist on the path; ZipFile zip = new ZipFile(ZipFile, charset.forname ("GBK")); byte[] buf1 = new byte[1024]; for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) { ZipEntry entry = (ZipEntry) entries.nextElement(); String zipEntryName = entry.getName(); try (InputStream in = zip.getInputStream(entry)) { String outPath = (descDir + zipEntryName).replaceAll("\\*", "/"); File File = new File(outpath.substring (0, outpath.lastIndexof ('/')))); if (! file.exists()) { file.mkdirs(); If (new File(outPath).isdirectory ()) {continue; } // Output file path information system.out.println (outPath); try (OutputStream out = new FileOutputStream(outPath)) { int len; while ((len = in.read(buf1)) > 0) { out.write(buf1, 0, len); }}}} System. Out. Println (" * * * * * * * * * * * * * * * * * * decompression is * * * * * * * * * * * * * * * * * * * * "); } public static void main(String[] args) throws IOException {/** * Decompress the File */ File zipFile = new File("E:\\video\\Learn\\Learn\\src\\main\\java\\com\\ploy\\abc.zip"); String path = "E:/video/Learn/Learn/src/main/java/com/ploy/"; unZipFiles(zipFile, path); }}Copy the code

5. Read JSON

Json read using the FastJSON library, simple to use, but also more convenient configuration, parsing is also more convenient.

Knowledge: reading files, using Fastjson

package com.ploy; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; /** * @author */ public class FileUtil {public static String readFile(String filePath) throws IOException {public static String readFile(String filePath) throws IOException { FileReader fileReader = new FileReader(filePath); BufferedReader bReader = new BufferedReader(fileReader); StringBuilder sb = new StringBuilder(); // define a String cache where String s = ""; while ((s = bReader.readLine()) ! Sb.append (s); Println (s); system.out.println (s); } bReader.close(); String jsonStr = sb.toString(); System.out.println(jsonStr); return jsonStr; }}Copy the code

6. Module organization

A screenshot of the compressed package

Ploymenu. json is the menu of all activities. The specific menu is the entire activity

Description of each field:

Pid: indicates the ID of an activity

Type: Indicates the activity type

Begin: indicates the start time of the activity

End: indicates the end time of the activity

Draw: is the activity can claim the prize time, generally should be greater than or equal to the activity end time

[{"begin": 2."draw": 4."end": 3."pid": 6."type": 6}]Copy the code

6. Json description:

6. Json is the specific activity information whose ACTIVITY ID is 6. You can customize each activity type as long as it is in JSON format

{” name “:” parsley “, “s” : 18}

7. Code presentation

Active object definition:

package com.ploy; import lombok.Getter; import lombok.Setter; @getter @setter public class PloyVO {// active ID private int pid; Private int begin; Private int end; Private int draw; Private int type; // Active detail Object private Object detail; }Copy the code

Activity enumeration definition:

package com.ploy; import com.ploy.detail.TestDetailVO; public enum PloyEnum { SEVEN_LOGIN(6, TestDetailVO.class) ; private int ployType; private Class detailClass; PloyEnum(int ployType, Class detailClass) { this.ployType = ployType; this.detailClass = detailClass; } public int getPloyType() { return ployType; } public Class getDetailClass() { return detailClass; } public static PloyEnum getPloyType(int type) { for (PloyEnum pt : values()) { if (pt.getPloyType() == type) { return pt; } } return null; }}Copy the code

Sample activity Details:

package com.ploy.detail; import lombok.Getter; import lombok.Setter; Public class TestDetailVO {private String name; public class TestDetailVO {private String name; private int s; }Copy the code

Activity Tools:

package com.ploy; import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; @slf4j public class PloyUtil {// private static Map<Integer, PloyVO> ployMap = Maps.newConcurrentMap(); public static void main(String[] args) { reloadPloy(); } public static void reloadPloy() { Map<Integer, PloyVO> tmpPloyMap = Maps.newConcurrentMap(); / / download / / asynchronous download DownloadUtil downloadByAsync (" http://localhost/ployPkg.zip ", "E:/video/Learn/Learn/src/main/java/com/ploy/unzip", "ployPkg.zip", New DownloadUtil. OnDownloadListener () {@ Override public void onDownloadSuccess (File File) {/ / decompressing files try {String foldPath = "E:\\video\\Learn\\Learn\\src\\main\\java\\com\\ploy\\unzip\\"; UnzipUtil.unZipFiles(file, foldPath); String jsonStr = FileUtil.readFile(foldPath + "/ployPkg/ployMenu.json"); List<PloyVO> ployList = JSON.parseArray(jsonStr, PloyVO.class); ParsePloy (PloyVO, foldPath + "/ployPkg"); for (PloyVO PloyVO: ployList) {parsePloy(PloyVO, foldPath + "/ployPkg"); tmpPloyMap.put(ployVO.getPid(), ployVO); } ployMap = tmpPloyMap; System.out.println("load finish"); } catch (IOException e) { e.printStackTrace(); }} @override public void onDownloading(int progress) {log.info(" downloading in progress "+ progress); } @override public void onDownloadFailed(Exception e) {log.error(" Download error ", e); }}); } private static void parsePloy(PloyVO ployVO, String folderPath) throws IOException { int type = ployVO.getType(); String s = FileUtil.readFile(folderPath + "/" + ployVO.getPid() + ".json"); PloyEnum ployType = PloyEnum.getPloyType(type); Object o = JSON.parseObject(s, ployType.getDetailClass()); ployVO.setDetail(o); } public static PloyVO getPloy(int pid) {PloyVO pv = ploymap.get (pid); if (pv == null) { log.error("not found ploy pid is " + pid); } return pv; } public static Boolean checkInPloyTime(PloyVO, PloyVO, PloyVO, PloyVO, PloyVO); int nowSec) { return pv.getBegin() <= nowSec && (pv.getEnd() == 0 || nowSec <= pv.getEnd()); } /** * Public static List<PloyVO> getPloyByType(PloyEnum ployEnum) { List<PloyVO> retList = Lists.newArrayList(); int nowSec = (int) (new Date().getTime() / 1000L); for (PloyVO pvo : ployMap.values()) { if (pvo.getType() == ployEnum.getPloyType() && checkInPloyTime(pvo, nowSec)) { retList.add(pvo); } } return retList; }}Copy the code

The entry point for active reloading is reloadPloy (), which calls Reload directly when active data needs to be reloaded,

Note: The new activity is loaded into memory before ployMap is overwritten

Run ployUtil and you can see that the data has been loaded into memory:

1. Encrypt and sign the activity data to prevent illegal persons from obtaining operation data

PloyUtil provides only a few simple structures, and you can add new interfaces as needed, such as retrieving data by activity type, or all currently open activities, etc., so that you can call them when you use them

3, and the client communication, when the player login can send the data of the activity to the client, so that the data and the server are consistent, each activity of their own communication can be. The client can judge by the time of the activity, or turn on the activity, or remove the icon of the activity.

4. The code only shows the idea, but there are some details that are not dealt with, such as exception handling, which can be adjusted according to the content of the project when used in the project

5. Some paths in the program and so on can be configured rather than written in code

9,

Knowledge:

OkHttp is used to download files asynchronously to local, DownloadUtil

Zip file decompression method, method, usually less used tool class, ZipUtil

To read files into strings, Java IO uses FileUtil

Use of fastJson to convert strings to lists,

Design patterns for activities, and how individual reads are used for each activity

Activity process:

Operation planning Activities

Run configuration activities and package them to a Web server

Notifies the server to load a new activity

Game server download activity to local

Decompress the active zip package

Read ploymenu. json to generate ployList

Generate activity details according to ployVO

The project source code download address: download.csdn.net/download/pe…