From df0a40b3c223a91d844d8369dd49e8ecd8ad51a7 Mon Sep 17 00:00:00 2001
From: Nev3r <apophycens@gmail.com>
Date: Sat, 1 Jun 2019 13:07:23 +0200
Subject: [PATCH] Code refactoring to turn portal struct into a more
 generalized shape.

Split portal-related code to its own source files.
Most of the 2-line-specific setup has been moved to the function which adds a 2-line case. The portals should render as they used to so far, anyway.
---
 src/CMakeLists.txt |   2 +
 src/Makefile       |   1 +
 src/r_bsp.c        |   3 +-
 src/r_bsp.h        |   1 -
 src/r_main.c       | 170 ++++++++---------------------------------
 src/r_plane.c      |  44 -----------
 src/r_plane.h      |   2 -
 src/r_portal.c     | 187 +++++++++++++++++++++++++++++++++++++++++++++
 src/r_portal.h     |  49 ++++++++++++
 src/r_things.c     |   4 +-
 10 files changed, 273 insertions(+), 190 deletions(-)
 create mode 100644 src/r_portal.c
 create mode 100644 src/r_portal.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a6fab34ff6..9e319d100e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -123,6 +123,7 @@ set(SRB2_CORE_RENDER_SOURCES
 	r_sky.c
 	r_splats.c
 	r_things.c
+	r_portal.c
 
 	r_bsp.h
 	r_data.h
@@ -136,6 +137,7 @@ set(SRB2_CORE_RENDER_SOURCES
 	r_splats.h
 	r_state.h
 	r_things.h
+	r_portal.h
 )
 
 set(SRB2_CORE_GAME_SOURCES
diff --git a/src/Makefile b/src/Makefile
index 5407a4d5ea..b8d91fcccd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -455,6 +455,7 @@ OBJS:=$(i_main_o) \
 		$(OBJDIR)/r_sky.o    \
 		$(OBJDIR)/r_splats.o \
 		$(OBJDIR)/r_things.o \
+		$(OBJDIR)/r_portal.o \
 		$(OBJDIR)/screen.o   \
 		$(OBJDIR)/v_video.o  \
 		$(OBJDIR)/s_sound.o  \
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 22abaeb88e..5643e777e1 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -26,7 +26,6 @@ side_t *sidedef;
 line_t *linedef;
 sector_t *frontsector;
 sector_t *backsector;
-boolean portalline; // is curline a portal seg?
 
 // very ugly realloc() of drawsegs at run-time, I upped it to 512
 // instead of 256.. and someone managed to send me a level with
@@ -459,7 +458,7 @@ static void R_AddLine(seg_t *line)
 				line2 = P_FindSpecialLineFromTag(40, line->linedef->tag, line2);
 			if (line2 >= 0) // found it!
 			{
-				R_AddPortal(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
+				Portal_Add2Lines(line->linedef-lines, line2, x1, x2); // Remember the lines for later rendering
 				//return; // Don't fill in that space now!
 				goto clipsolid;
 			}
diff --git a/src/r_bsp.h b/src/r_bsp.h
index e3662e2e6a..50fea6681a 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -38,7 +38,6 @@ void R_ClearClipSegs(void);
 void R_PortalClearClipSegs(INT32 start, INT32 end);
 void R_ClearDrawSegs(void);
 void R_RenderBSPNode(INT32 bspnum);
-void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2);
 
 #ifdef POLYOBJECTS
 void R_SortPolyObjects(subsector_t *sub);
diff --git a/src/r_main.c b/src/r_main.c
index 23dc39d62f..cab0490bfb 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -30,6 +30,7 @@
 #include "p_spec.h" // skyboxmo
 #include "z_zone.h"
 #include "m_random.h" // quake camera shake
+#include "r_portal.h"
 
 #ifdef HWRENDER
 #include "hardware/hw_main.h"
@@ -70,32 +71,6 @@ boolean skyVisible1, skyVisible2; // saved values of skyVisible for P1 and P2, f
 sector_t *viewsector;
 player_t *viewplayer;
 
-// PORTALS!
-// You can thank and/or curse JTE for these.
-UINT8 portalrender;
-sector_t *portalcullsector;
-typedef struct portal_pair
-{
-	INT32 line1;
-	INT32 line2;
-	UINT8 pass;
-	struct portal_pair *next;
-
-	fixed_t viewx;
-	fixed_t viewy;
-	fixed_t viewz;
-	angle_t viewangle;
-
-	INT32 start;
-	INT32 end;
-	INT16 *ceilingclip;
-	INT16 *floorclip;
-	fixed_t *frontscale;
-} portal_pair;
-portal_pair *portal_base, *portal_cap;
-line_t *portalclipline;
-INT32 portalclipstart, portalclipend;
-
 //
 // precalculated math tables
 //
@@ -1010,17 +985,8 @@ void R_SkyboxFrame(player_t *player)
 	R_SetupFreelook();
 }
 
-#define ANGLED_PORTALS
-
-static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal)
+static void R_PortalFrame(portal_t *portal)
 {
-	vertex_t dest_c, start_c;
-#ifdef ANGLED_PORTALS
-	// delta angle
-	angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
-#endif
-
-	//R_SetupFrame(player, false);
 	viewx = portal->viewx;
 	viewy = portal->viewy;
 	viewz = portal->viewz;
@@ -1029,93 +995,18 @@ static void R_PortalFrame(line_t *start, line_t *dest, portal_pair *portal)
 	viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
 	viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
 
-	portalcullsector = dest->frontsector;
-	viewsector = dest->frontsector;
-	portalclipline = dest;
 	portalclipstart = portal->start;
 	portalclipend = portal->end;
 
-	// Offset the portal view by the linedef centers
-
-	// looking glass center
-	start_c.x = (start->v1->x + start->v2->x) / 2;
-	start_c.y = (start->v1->y + start->v2->y) / 2;
-
-	// other side center
-	dest_c.x = (dest->v1->x + dest->v2->x) / 2;
-	dest_c.y = (dest->v1->y + dest->v2->y) / 2;
-
-	// Heights!
-	viewz += dest->frontsector->floorheight - start->frontsector->floorheight;
-
-	// calculate the difference in position and rotation!
-#ifdef ANGLED_PORTALS
-	if (dangle == 0)
-#endif
-	{ // the entrance goes straight opposite the exit, so we just need to mess with the offset.
-		viewx += dest_c.x - start_c.x;
-		viewy += dest_c.y - start_c.y;
-		return;
-	}
-
-#ifdef ANGLED_PORTALS
-	viewangle += dangle;
-	viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
-	viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
-	//CONS_Printf("dangle == %u\n", AngleFixed(dangle)>>FRACBITS);
-
-	// ????
-	{
-		fixed_t disttopoint;
-		angle_t angtopoint;
-
-		disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy);
-		angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy);
-		angtopoint += dangle;
-
-		viewx = dest_c.x+FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
-		viewy = dest_c.y+FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
-	}
-#endif
-}
-
-void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2)
-{
-	portal_pair *portal = Z_Malloc(sizeof(portal_pair), PU_LEVEL, NULL);
-	INT16 *ceilingclipsave = Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
-	INT16 *floorclipsave = Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
-	fixed_t *frontscalesave = Z_Malloc(sizeof(fixed_t)*(x2-x1), PU_LEVEL, NULL);
-
-	portal->line1 = line1;
-	portal->line2 = line2;
-	portal->pass = portalrender+1;
-	portal->next = NULL;
-
-	R_PortalStoreClipValues(x1, x2, ceilingclipsave, floorclipsave, frontscalesave);
-
-	portal->ceilingclip = ceilingclipsave;
-	portal->floorclip = floorclipsave;
-	portal->frontscale = frontscalesave;
-
-	portal->start = x1;
-	portal->end = x2;
-
-	portalline = true; // this tells R_StoreWallRange that curline is a portal seg
-
-	portal->viewx = viewx;
-	portal->viewy = viewy;
-	portal->viewz = viewz;
-	portal->viewangle = viewangle;
-
-	if (!portal_base)
+	if (portal->clipline != -1)
 	{
-		portal_base = portal;
-		portal_cap = portal;
+		portalclipline = &lines[portal->clipline];
+		viewsector = portalcullsector = portalclipline->frontsector;
 	}
 	else
 	{
-		portal_cap->next = portal;
-		portal_cap = portal;
+		portalclipline = NULL;
+		viewsector = portalcullsector = R_PointInSubsector(viewx, viewy)->sector;
 	}
 }
 
@@ -1131,7 +1022,7 @@ void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2)
 
 void R_RenderPlayerView(player_t *player)
 {
-	portal_pair *portal;
+	portal_t *portal;
 	const boolean skybox = (skyboxmo[0] && cv_skybox.value);
 
 	if (cv_homremoval.value && player == &players[displayplayer]) // if this is display player 1
@@ -1148,8 +1039,7 @@ void R_RenderPlayerView(player_t *player)
 	else
 		skyVisible = skyVisible1;
 
-	portalrender = 0;
-	portal_base = portal_cap = NULL;
+	Portal_InitList();
 
 	if (skybox && skyVisible)
 	{
@@ -1206,35 +1096,37 @@ void R_RenderPlayerView(player_t *player)
 #endif
 //profile stuff ---------------------------------------------------------
 
-	// PORTAL RENDERING
-	for(portal = portal_base; portal; portal = portal_base)
+	// Portal rendering. Hijacks the BSP traversal.
+	if (portal_base)
 	{
-		// render the portal
-		CONS_Debug(DBG_RENDER, "Rendering portal from line %d to %d\n", portal->line1, portal->line2);
-		portalrender = portal->pass;
+		for(portal = portal_base; portal; portal = portal_base)
+		{
+			portalrender = portal->pass; // Recursiveness depth.
 
-		R_PortalFrame(&lines[portal->line1], &lines[portal->line2], portal);
+			// Apply the viewpoint stored for the portal.
+			R_PortalFrame(portal);
 
-		R_PortalClearClipSegs(portal->start, portal->end);
+			// Hack in the clipsegs to delimit the starting
+			// clipping for sprites and possibly other similar
+			// future items.
+			R_PortalClearClipSegs(portal->start, portal->end);
 
-		R_PortalRestoreClipValues(portal->start, portal->end, portal->ceilingclip, portal->floorclip, portal->frontscale);
+			// Hack in the top/bottom clip values for the window
+			// that were previously stored.
+			Portal_ClipApply(portal);
 
-		validcount++;
+			// Render the BSP from the new viewpoint, and clip
+			// any sprites with the new clipsegs and window.
+			R_RenderBSPNode((INT32)numnodes - 1);
+			R_ClipSprites();
 
-		R_RenderBSPNode((INT32)numnodes - 1);
-		R_ClipSprites();
-		//R_DrawPlanes();
-		//R_DrawMasked();
+			Portal_Remove(portal);
+
+			validcount++;
+		}
 
-		// okay done. free it.
 		portalcullsector = NULL; // Just in case...
-		portal_base = portal->next;
-		Z_Free(portal->ceilingclip);
-		Z_Free(portal->floorclip);
-		Z_Free(portal->frontscale);
-		Z_Free(portal);
 	}
-	// END PORTAL RENDERING
 
 	R_DrawPlanes();
 #ifdef FLOORSPLATS
diff --git a/src/r_plane.c b/src/r_plane.c
index 0ef4c2c05b..5cd9b26b5a 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -112,50 +112,6 @@ void R_InitPlanes(void)
 	// FIXME: unused
 }
 
-// R_PortalStoreClipValues
-// Saves clipping values for later. -Red
-void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale)
-{
-	INT32 i;
-	for (i = 0; i < end-start; i++)
-	{
-		*ceil = ceilingclip[start+i];
-		ceil++;
-		*floor = floorclip[start+i];
-		floor++;
-		*scale = frontscale[start+i];
-		scale++;
-	}
-}
-
-// R_PortalRestoreClipValues
-// Inverse of the above. Restores the old value!
-void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale)
-{
-	INT32 i;
-	for (i = 0; i < end-start; i++)
-	{
-		ceilingclip[start+i] = *ceil;
-		ceil++;
-		floorclip[start+i] = *floor;
-		floor++;
-		frontscale[start+i] = *scale;
-		scale++;
-	}
-
-	// HACKS FOLLOW
-	for (i = 0; i < start; i++)
-	{
-		floorclip[i] = -1;
-		ceilingclip[i] = (INT16)viewheight;
-	}
-	for (i = end; i < vid.width; i++)
-	{
-		floorclip[i] = -1;
-		ceilingclip[i] = (INT16)viewheight;
-	}
-}
-
 //
 // R_MapPlane
 //
diff --git a/src/r_plane.h b/src/r_plane.h
index 6e6a6d49d0..bdfc40058c 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -72,8 +72,6 @@ extern fixed_t *yslope;
 extern lighttable_t **planezlight;
 
 void R_InitPlanes(void);
-void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale);
-void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale);
 void R_ClearPlanes(void);
 
 void R_MapPlane(INT32 y, INT32 x1, INT32 x2);
diff --git a/src/r_portal.c b/src/r_portal.c
new file mode 100644
index 0000000000..2675ae968c
--- /dev/null
+++ b/src/r_portal.c
@@ -0,0 +1,187 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  r_portal.c
+/// \brief Software renderer portals.
+
+#include "r_portal.h"
+#include "r_plane.h"
+#include "r_main.h" // viewheight, viewwidth
+#include "z_zone.h"
+
+UINT8 portalrender;			/**< When rendering a portal, it establishes the depth of the current BSP traversal. */
+sector_t *portalcullsector;
+
+// Linked list for portals.
+portal_t *portal_base, *portal_cap;
+
+line_t *portalclipline;
+INT32 portalclipstart, portalclipend;
+
+boolean portalline; // is curline a portal seg?
+
+void Portal_InitList (void)
+{
+	portalrender = 0;
+	portal_base = portal_cap = NULL;
+}
+
+/** Store the clipping window for a portal in its given range.
+ *
+ * The window is copied from the current window at the time
+ * the function is called, so it is useful for converting one-sided
+ * lines into portals.
+ */
+void Portal_ClipStoreFromRange (portal_t* portal)
+{
+	INT32 start	= portal->start;
+	INT32 end	= portal->end;
+	INT16 *ceil		= portal->ceilingclip;
+	INT16 *floor	= portal->floorclip;
+	fixed_t *scale	= portal->frontscale;
+
+	INT32 i;
+	for (i = 0; i < end-start; i++)
+	{
+		*ceil = ceilingclip[start+i];
+		ceil++;
+		*floor = floorclip[start+i];
+		floor++;
+		*scale = frontscale[start+i];
+		scale++;
+	}
+}
+
+/** Apply the clipping window from a portal.
+ */
+void Portal_ClipApply (const portal_t* portal)
+{
+	INT32 i;
+	INT32 start	= portal->start;
+	INT32 end	= portal->end;
+	INT16 *ceil		= portal->ceilingclip;
+	INT16 *floor	= portal->floorclip;
+	fixed_t *scale	= portal->frontscale;
+
+	for (i = 0; i < end-start; i++)
+	{
+		ceilingclip[start+i] = *ceil;
+		ceil++;
+		floorclip[start+i] = *floor;
+		floor++;
+		frontscale[start+i] = *scale;
+		scale++;
+	}
+
+	// HACKS FOLLOW
+	for (i = 0; i < start; i++)
+	{
+		floorclip[i] = -1;
+		ceilingclip[i] = (INT16)viewheight;
+	}
+	for (i = end; i < vid.width; i++)
+	{
+		floorclip[i] = -1;
+		ceilingclip[i] = (INT16)viewheight;
+	}
+}
+
+portal_t* Portal_Add (const INT16 x1, const INT16 x2)
+{
+	portal_t *portal		= Z_Malloc(sizeof(portal_t), PU_LEVEL, NULL);
+	INT16 *ceilingclipsave	= Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
+	INT16 *floorclipsave	= Z_Malloc(sizeof(INT16)*(x2-x1), PU_LEVEL, NULL);
+	fixed_t *frontscalesave	= Z_Malloc(sizeof(fixed_t)*(x2-x1), PU_LEVEL, NULL);
+
+	// Linked list.
+	if (!portal_base)
+	{
+		portal_base	= portal;
+		portal_cap	= portal;
+	}
+	else
+	{
+		portal_cap->next = portal;
+		portal_cap = portal;
+	}
+	portal->next = NULL;
+
+	// Store clipping values so they can be restored once the portal is rendered.
+	portal->ceilingclip	= ceilingclipsave;
+	portal->floorclip	= floorclipsave;
+	portal->frontscale	= frontscalesave;
+	portal->start	= x1;
+	portal->end		= x2;
+
+	// Increase recursion level.
+	portal->pass = portalrender+1;
+
+	return portal;
+}
+
+void Portal_Remove (portal_t* portal)
+{
+	portal_base = portal->next;
+	Z_Free(portal->ceilingclip);
+	Z_Free(portal->floorclip);
+	Z_Free(portal->frontscale);
+	Z_Free(portal);
+}
+
+/** Creates a portal out of two lines and a determined screen range.
+ *
+ * line1 determines the entrance, and line2 the exit.
+ * x1 and x2 determine the screen's column bounds.
+
+ * The view's offset from the entry line center is obtained,
+ * and then rotated&translated to the exit line's center.
+ * When the portal renders, it will create the illusion of
+ * the two lines being seamed together.
+ */
+void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2)
+{
+	portal_t* portal = Portal_Add(x1, x2);
+
+	// Offset the portal view by the linedef centers
+	line_t* start	= &lines[line1];
+	line_t* dest	= &lines[line2];
+
+	angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0);
+
+	fixed_t disttopoint;
+	angle_t angtopoint;
+
+	vertex_t dest_c, start_c;
+
+	// looking glass center
+	start_c.x = (start->v1->x + start->v2->x) / 2;
+	start_c.y = (start->v1->y + start->v2->y) / 2;
+
+	// other side center
+	dest_c.x = (dest->v1->x + dest->v2->x) / 2;
+	dest_c.y = (dest->v1->y + dest->v2->y) / 2;
+
+	disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy);
+	angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy);
+	angtopoint += dangle;
+
+	portal->viewx = dest_c.x + FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
+	portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint);
+	portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight;
+	portal->viewangle = viewangle + dangle;
+
+	portal->clipline = line2;
+
+	Portal_ClipStoreFromRange(portal);
+
+	portalline = true; // this tells R_StoreWallRange that curline is a portal seg
+}
+
+
diff --git a/src/r_portal.h b/src/r_portal.h
new file mode 100644
index 0000000000..8df3db415c
--- /dev/null
+++ b/src/r_portal.h
@@ -0,0 +1,49 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  r_portal.h
+/// \brief Software renderer portal struct, functions, linked list extern.
+
+#include "r_data.h"
+
+
+/** Portal structure.
+ */
+typedef struct portal_s
+{
+	struct portal_s *next;
+
+	// Viewport.
+	fixed_t viewx;
+	fixed_t viewy;
+	fixed_t viewz;
+	angle_t viewangle;
+
+	UINT8 pass;			/**< Keeps track of the portal's recursion depth. */
+	INT32 clipline;		/**< Optional clipline for line-based portals. */
+
+	// Clipping information.
+	INT32 start;		/**< First horizontal pixel coordinate to draw at. */
+	INT32 end;			/**< Last horizontal pixel coordinate to draw at. */
+	INT16 *ceilingclip; /**< Temporary screen top clipping array. */
+	INT16 *floorclip;	/**< Temporary screen bottom clipping array. */
+	fixed_t *frontscale;/**< Temporary screen bottom clipping array. */
+} portal_t;
+
+extern portal_t* portal_base;
+extern portal_t* portal_cap;
+extern UINT8 portalrender;
+
+void Portal_InitList	(void);
+void Portal_Remove		(portal_t* portal);
+void Portal_Add2Lines	(const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2);
+
+void Portal_ClipStoreFromRange (portal_t* portal);
+void Portal_ClipApply (const portal_t* portal);
diff --git a/src/r_things.c b/src/r_things.c
index e8d679b539..f52ce7ca82 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1249,7 +1249,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	// PORTAL SPRITE CLIPPING
-	if (portalrender)
+	if (portalrender && portalclipline)
 	{
 		if (x2 < portalclipstart || x1 > portalclipend)
 			return;
@@ -1517,7 +1517,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 		return;
 
 	// PORTAL SPRITE CLIPPING
-	if (portalrender)
+	if (portalrender && portalclipline)
 	{
 		if (x2 < portalclipstart || x1 > portalclipend)
 			return;
-- 
GitLab